├── client
├── src
│ ├── page
│ │ ├── chat1.jsx
│ │ ├── index.js
│ │ ├── error.jsx
│ │ ├── login.jsx
│ │ ├── forgot.jsx
│ │ ├── signup.jsx
│ │ ├── chat.jsx
│ │ └── style.scss
│ ├── config
│ │ └── instance.js
│ ├── assets
│ │ ├── tick.jsx
│ │ ├── stop.jsx
│ │ ├── message.jsx
│ │ ├── plus.jsx
│ │ ├── xIcon.jsx
│ │ ├── thunder.jsx
│ │ ├── avatar.jsx
│ │ ├── rocket.jsx
│ │ ├── bar.jsx
│ │ ├── logOut.jsx
│ │ ├── tab.jsx
│ │ ├── reload.jsx
│ │ ├── trash.jsx
│ │ ├── warning.jsx
│ │ ├── microsoft.jsx
│ │ ├── mail.jsx
│ │ ├── sun.jsx
│ │ ├── File.jsx
│ │ ├── settings.jsx
│ │ ├── eye.jsx
│ │ ├── index.js
│ │ ├── upload.jsx
│ │ ├── eyeHide.jsx
│ │ ├── google.jsx
│ │ ├── Profile.jsx
│ │ ├── gptIcon.jsx
│ │ └── grant.jsx
│ ├── redux
│ │ ├── store.js
│ │ ├── loading.js
│ │ ├── user.js
│ │ ├── history.js
│ │ └── messages.js
│ ├── components
│ │ ├── index.js
│ │ ├── loading
│ │ │ ├── loading.jsx
│ │ │ └── style.scss
│ │ ├── auth
│ │ │ ├── registerPendings.jsx
│ │ │ ├── FormFeild.jsx
│ │ │ ├── signup.jsx
│ │ │ ├── login.jsx
│ │ │ └── forgot.jsx
│ │ ├── content
│ │ │ ├── new.jsx
│ │ │ ├── chat.jsx
│ │ │ └── style.scss
│ │ └── menu
│ │ │ ├── style.scss
│ │ │ └── menu.jsx
│ ├── main.jsx
│ ├── index.scss
│ ├── protected.jsx
│ └── App.jsx
├── public
│ ├── _redirects
│ ├── favicon.png
│ ├── manifest
│ │ ├── icon-192x192.png
│ │ ├── icon-256x256.png
│ │ ├── icon-384x384.png
│ │ └── icon-512x512.png
│ └── logo.svg
├── Dockerfile
├── index.html
├── package.json
├── vite.config.js
└── .gitignore
├── server
├── db
│ ├── collections.js
│ └── connection.js
├── Dockerfile
├── package.json
├── mail
│ ├── send.js
│ ├── template.html
│ └── otp.js
├── app.js
├── .gitignore
├── helpers
│ └── chat.js
└── routes
│ └── chat.js
├── docker-compose.yml
└── README.md
/client/src/page/chat1.jsx:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/public/_redirects:
--------------------------------------------------------------------------------
1 | /* /index.html 200
2 |
--------------------------------------------------------------------------------
/server/db/collections.js:
--------------------------------------------------------------------------------
1 | export default {
2 | CHAT: 'chat',
3 | USER: 'user',
4 | TEMP: 'temp'
5 | }
--------------------------------------------------------------------------------
/client/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanzalahwaheed/Proton-Screening-Test/HEAD/client/public/favicon.png
--------------------------------------------------------------------------------
/client/public/manifest/icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanzalahwaheed/Proton-Screening-Test/HEAD/client/public/manifest/icon-192x192.png
--------------------------------------------------------------------------------
/client/public/manifest/icon-256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanzalahwaheed/Proton-Screening-Test/HEAD/client/public/manifest/icon-256x256.png
--------------------------------------------------------------------------------
/client/public/manifest/icon-384x384.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanzalahwaheed/Proton-Screening-Test/HEAD/client/public/manifest/icon-384x384.png
--------------------------------------------------------------------------------
/client/public/manifest/icon-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanzalahwaheed/Proton-Screening-Test/HEAD/client/public/manifest/icon-512x512.png
--------------------------------------------------------------------------------
/client/src/config/instance.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | const instance = axios.create({
4 | withCredentials: true,
5 | baseURL: "http://localhost"
6 | })
7 | export default instance
--------------------------------------------------------------------------------
/client/src/page/index.js:
--------------------------------------------------------------------------------
1 | export { default as Main } from './chat'
2 | export { default as Error } from './error'
3 | export { default as Login } from './login'
4 | export { default as Signup } from './signup'
5 | export { default as Forgot } from './forgot'
--------------------------------------------------------------------------------
/client/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:20.11.0-alpine
2 | RUN mkdir -p /client
3 | WORKDIR /client
4 |
5 | COPY package.json .
6 | COPY package-lock.json .
7 |
8 | RUN npm install
9 | RUN npm rebuild bcrypt
10 |
11 | COPY . .
12 |
13 | EXPOSE 80
14 |
15 | CMD npm run dev
--------------------------------------------------------------------------------
/server/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:20.11.0-alpine
2 | RUN mkdir -p /server
3 | WORKDIR /server
4 |
5 | COPY package.json .
6 | COPY package-lock.json .
7 |
8 | RUN npm install
9 | RUN npm rebuild bcrypt
10 | RUN npm install nodeman
11 |
12 | COPY . .
13 |
14 | EXPOSE 5000
15 |
16 | CMD ["npm", "start"]
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.9'
2 | services:
3 | server:
4 | build:
5 | context: ./server
6 | dockerfile: Dockerfile
7 | ports:
8 | - '5000:5000'
9 | client:
10 | build:
11 | context: ./client
12 | dockerfile: Dockerfile
13 | ports:
14 | - '80:80'
15 | links:
16 | - server
17 |
--------------------------------------------------------------------------------
/client/src/assets/tick.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Tick = () => {
4 | return (
5 |
6 | )
7 | }
8 |
9 | export default Tick
--------------------------------------------------------------------------------
/client/src/redux/store.js:
--------------------------------------------------------------------------------
1 | import { configureStore } from "@reduxjs/toolkit";
2 | import user from "./user";
3 | import loading from "./loading";
4 | import history from "./history";
5 | import messages from "./messages";
6 |
7 | export const store = configureStore({
8 | reducer: {
9 | user,
10 | loading,
11 | history,
12 | messages
13 | }
14 | })
--------------------------------------------------------------------------------
/server/db/connection.js:
--------------------------------------------------------------------------------
1 | import { MongoClient } from "mongodb";
2 |
3 | let db = null
4 |
5 | const connectDB = async (done) => {
6 | try {
7 | var data = await MongoClient.connect(process.env.MONGO_URL, { useNewUrlParser: true })
8 | db = data.db('chatGPT')
9 | done()
10 | } catch (err) {
11 | done(err)
12 | }
13 | }
14 |
15 | export { connectDB, db }
--------------------------------------------------------------------------------
/client/src/assets/stop.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Stop = () => {
4 | return (
5 |
6 | )
7 | }
8 |
9 | export default Stop
10 |
--------------------------------------------------------------------------------
/client/src/assets/message.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Message = () => {
4 | return (
5 |
6 | )
7 | }
8 |
9 | export default Message
--------------------------------------------------------------------------------
/client/src/assets/plus.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Plus = () => {
4 | return (
5 |
6 | )
7 | }
8 |
9 | export default Plus
--------------------------------------------------------------------------------
/client/src/assets/xIcon.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Xicon = () => {
4 | return (
5 |
6 | )
7 | }
8 |
9 | export default Xicon
--------------------------------------------------------------------------------
/client/src/assets/thunder.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Thunder = () => {
4 | return (
5 |
6 | )
7 | }
8 |
9 | export default Thunder
--------------------------------------------------------------------------------
/client/src/components/index.js:
--------------------------------------------------------------------------------
1 | export { default as Chat } from './content/chat'
2 | export { default as New } from './content/new'
3 | export { default as Menu } from './menu/menu'
4 | export { default as RegisterPendings } from './auth/registerPendings'
5 | export { default as LoginComponent } from './auth/login'
6 | export { default as SignupComponent } from './auth/signup'
7 | export { default as ForgotComponent } from './auth/forgot'
--------------------------------------------------------------------------------
/client/src/assets/avatar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Avatar = () => {
4 | return (
5 |
6 | )
7 | }
8 |
9 | export default Avatar
--------------------------------------------------------------------------------
/client/src/assets/rocket.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Rocket = () => {
4 | return (
5 |
6 | )
7 | }
8 |
9 | export default Rocket
--------------------------------------------------------------------------------
/client/src/redux/loading.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from "@reduxjs/toolkit";
2 |
3 | let loadingSlice = createSlice({
4 | name: 'loading',
5 | initialState: true,
6 | reducers: ({
7 | setLoading: (state, { payload }) => {
8 | state = payload
9 | return state
10 | }
11 | })
12 | })
13 |
14 | export const { setLoading } = loadingSlice.actions
15 |
16 | export default loadingSlice.reducer
--------------------------------------------------------------------------------
/client/src/assets/bar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Bar = () => {
4 | return (
5 |
6 | )
7 | }
8 |
9 | export default Bar
--------------------------------------------------------------------------------
/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | GE CoPilot™
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/client/src/assets/logOut.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const LogOut = () => {
4 | return (
5 |
6 | )
7 | }
8 |
9 | export default LogOut
--------------------------------------------------------------------------------
/client/src/assets/tab.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Tab = () => {
4 | return (
5 |
6 | )
7 | }
8 |
9 | export default Tab
--------------------------------------------------------------------------------
/client/src/components/loading/loading.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Grant } from '../../assets'
3 | import './style.scss'
4 |
5 | const Loading = () => {
6 | return (
7 |
8 |
9 |
10 |
Please stand by, while we are checking your browser...
11 |
12 |
13 | )
14 | }
15 |
16 | export default Loading
17 |
--------------------------------------------------------------------------------
/client/src/redux/user.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit'
2 |
3 | let userSlice = createSlice({
4 | name: 'user',
5 | initialState: null,
6 | reducers: {
7 | insertUser: (state, action) => {
8 | return action.payload
9 | },
10 | emptyUser: (state, action) => {
11 | return null
12 | }
13 | }
14 | })
15 |
16 | export const { insertUser, emptyUser } = userSlice.actions
17 |
18 | export default userSlice.reducer
--------------------------------------------------------------------------------
/client/src/assets/reload.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Reload = () => {
4 | return (
5 |
6 | )
7 | }
8 |
9 | export default Reload
10 |
--------------------------------------------------------------------------------
/client/src/assets/trash.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Trash = () => {
4 | return (
5 |
6 | )
7 | }
8 |
9 | export default Trash
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # test
2 | For screening test, clone the repo and follow the steps to run the app.
3 |
4 | How to run the app:
5 |
6 | 1. Ensure npm is Installed:
7 | Before starting, make sure that npm (Node Package Manager) is installed on your system.
8 | 2. Running the Server:
9 | Navigate to the server directory within the application's root folder using a command line tool.
10 | Execute the command npm start.
11 | 3. Running the Client:
12 | Open a new command line interface.Navigate to the directory.
13 | Execute the command npm run dev.
14 |
15 |
--------------------------------------------------------------------------------
/client/src/assets/warning.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Warning = () => {
4 | return (
5 |
6 | )
7 | }
8 |
9 | export default Warning
--------------------------------------------------------------------------------
/client/src/assets/microsoft.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Microsoft = () => {
4 | return (
5 |
17 | )
18 | }
19 |
20 | export default Microsoft
21 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chatgpt",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "type": "module",
7 | "scripts": {
8 | "start": "nodemon app.js"
9 | },
10 | "author": "Yash Deep",
11 | "license": "ISC",
12 | "dependencies": {
13 | "aws-sdk": "^2.1560.0",
14 | "axios": "^1.3.5",
15 | "bcrypt": "^5.1.0",
16 | "cookie-parser": "^1.4.6",
17 | "cors": "^2.8.5",
18 | "dotenv": "^16.0.3",
19 | "express": "^4.18.2",
20 | "jsonwebtoken": "^9.0.0",
21 | "mongodb": "^5.1.0",
22 | "multer": "^1.4.5-lts.1",
23 | "nodemailer": "^6.9.13",
24 | "nodemon": "^2.0.22",
25 | "openai": "^4.28.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/client/src/main.jsx:
--------------------------------------------------------------------------------
1 | import { GoogleOAuthProvider } from '@react-oauth/google'
2 | import React from 'react'
3 | import ReactDOM from 'react-dom/client'
4 | import { Provider } from 'react-redux'
5 | import { store } from './redux/store'
6 | import App from './App'
7 | import { BrowserRouter } from 'react-router-dom'
8 | import './index.scss'
9 |
10 | ReactDOM.createRoot(document.getElementById('root')).render(
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | ,
20 | )
21 |
--------------------------------------------------------------------------------
/client/src/assets/mail.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Mail = () => {
4 | return (
5 |
17 | )
18 | }
19 |
20 | export default Mail
21 |
--------------------------------------------------------------------------------
/client/src/assets/sun.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Sun = () => {
4 | return (
5 |
6 | )
7 | }
8 |
9 | export default Sun
--------------------------------------------------------------------------------
/server/mail/send.js:
--------------------------------------------------------------------------------
1 | import nodemailer from 'nodemailer'
2 | import dotenv from 'dotenv';
3 |
4 | dotenv.config();
5 |
6 | const transporter = nodemailer.createTransport({
7 | service: 'gmail',
8 | // host:"smtp.gmail.com",
9 | auth: {
10 | user: process.env.MAIL_EMAIL,
11 | pass: process.env.MAIL_SECRET
12 | }
13 | })
14 |
15 | export default ({ to, subject, html }) => {
16 | const options = {
17 | from: `GE CoPilot™ <${process.env.MAIL_EMAIL}>`,
18 | to,
19 | subject,
20 | html
21 | }
22 |
23 | transporter.sendMail(options, (err, done) =>{
24 | if (err) {
25 | console.error('Failed to send email:',err);
26 | } else {
27 | console.log('Email sent: ', done?.response);
28 | }
29 | });
30 | }
--------------------------------------------------------------------------------
/client/src/page/error.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { useDispatch } from 'react-redux'
3 | import { setLoading } from '../redux/loading'
4 | import './style.scss'
5 |
6 | const Error = ({ status, content }) => {
7 | const dispatch = useDispatch()
8 |
9 | useEffect(() => {
10 | setTimeout(() => {
11 | dispatch(setLoading({ site: false }))
12 | }, 1000)
13 | }, [])
14 | return (
15 |
16 |
17 |
18 | {status}
19 |
20 |
21 | {content}
22 |
23 |
24 |
25 | )
26 | }
27 |
28 | export default Error
29 |
--------------------------------------------------------------------------------
/client/src/assets/File.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const File = () => {
4 | return (
5 | <>
6 |
15 | >
16 | );
17 | };
18 |
19 | export default File;
20 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "private": true,
4 | "version": "1.0.0",
5 | "author": "Yash Deep",
6 | "type": "module",
7 | "scripts": {
8 | "dev": "vite",
9 | "build": "vite build",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@react-oauth/google": "^0.9.0",
14 | "@reduxjs/toolkit": "^1.9.3",
15 | "axios": "^1.3.4",
16 | "buffer": "^6.0.3",
17 | "dotenv": "^16.4.4",
18 | "fs": "^0.0.1-security",
19 | "openai": "^4.28.0",
20 | "react": "^18.2.0",
21 | "react-dom": "^18.2.0",
22 | "react-redux": "^8.0.5",
23 | "react-router-dom": "^6.9.0",
24 | "react-s3": "^1.3.1",
25 | "sass": "^1.59.3"
26 | },
27 | "devDependencies": {
28 | "@types/react": "^18.0.28",
29 | "@types/react-dom": "^18.0.11",
30 | "@vitejs/plugin-react": "^3.1.0",
31 | "vite": "^4.2.0",
32 | "vite-plugin-pwa": "^0.14.7"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/client/src/redux/history.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from "@reduxjs/toolkit";
2 |
3 | let historySlice = createSlice({
4 | name: 'history',
5 | initialState: [],
6 | reducers: {
7 | addHistory: (state, { payload }) => {
8 | return payload
9 | },
10 | activePage: (state, { payload = null }) => {
11 | let pos = null
12 | state.forEach((obj, index) => {
13 | if (obj.chatId === payload) {
14 | obj.active = true
15 | pos = index
16 | } else {
17 | obj.active = false
18 | }
19 | })
20 |
21 | if (pos) {
22 | let obj = state[pos]
23 | state.splice(pos,1)
24 | state.unshift(obj)
25 | }
26 |
27 | return state
28 | }
29 | }
30 | })
31 |
32 | export const { addHistory, activePage } = historySlice.actions
33 |
34 | export default historySlice.reducer
--------------------------------------------------------------------------------
/client/src/assets/settings.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Settings = () => {
4 | return (
5 |
6 | )
7 | }
8 |
9 | export default Settings
10 |
--------------------------------------------------------------------------------
/client/src/assets/eye.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Eye = () => {
4 | return (
5 |
24 | )
25 | }
26 |
27 | export default Eye
28 |
--------------------------------------------------------------------------------
/client/src/assets/index.js:
--------------------------------------------------------------------------------
1 | export { default as Thunder } from './thunder'
2 | export { default as Sun } from './sun'
3 | export { default as Warning } from './warning'
4 | export { default as Rocket } from './rocket'
5 | export { default as Plus } from './plus'
6 | export { default as Avatar } from './avatar'
7 | export { default as Tab } from './tab'
8 | export { default as LogOut } from './logOut'
9 | export { default as Xicon } from './xIcon'
10 | export { default as Bar } from './bar'
11 | export { default as Trash } from './trash'
12 | export { default as Tick } from './tick'
13 | export { default as Message } from './message'
14 | export { default as GptIcon } from './gptIcon'
15 | export { default as Reload } from './reload'
16 | export { default as Stop } from './stop'
17 | export { default as Google } from './google'
18 | export { default as Microsoft } from './microsoft'
19 | export { default as Eye } from './eye'
20 | export { default as EyeHide } from './eyeHide'
21 | export { default as Mail } from './mail'
22 | export { default as Settings } from './settings'
23 | export { default as Grant } from './grant'
24 | export { default as File } from './File'
--------------------------------------------------------------------------------
/client/src/components/loading/style.scss:
--------------------------------------------------------------------------------
1 | .light {
2 | div[data-for='Loading'] {
3 | background: white;
4 |
5 | svg {
6 | color: #343541;
7 | }
8 |
9 | div {
10 | color: #343541;
11 | }
12 | }
13 | }
14 |
15 | .dark {
16 | div[data-for='Loading'] {
17 | background: rgba(52, 53, 65, 1);
18 |
19 | svg {
20 | color: #D9D9E3;
21 | }
22 |
23 | div {
24 | color: #D9D9E3;
25 | }
26 | }
27 | }
28 |
29 | div[data-for='Loading'] {
30 | display: block;
31 | position: fixed;
32 | height: 100%;
33 | width: 100%;
34 | z-index: 3000;
35 |
36 | .inner {
37 | display: flex;
38 | flex-direction: column;
39 | height: 100%;
40 | width: 100%;
41 | align-items: center;
42 | justify-content: center;
43 |
44 | svg {
45 | height: 2.5rem;
46 | width: 2.5rem;
47 | }
48 |
49 | div[data-for="text"] {
50 | text-align: center;
51 | padding: 1em;
52 | font-size: 16px;
53 | line-height: 24px;
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/server/app.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import cors from 'cors'
3 | import cookieParser from 'cookie-parser'
4 | import dotnet from 'dotenv'
5 | import { connectDB } from './db/connection.js'
6 | import ChatRoute from './routes/chat.js'
7 | import UserRoute from './routes/user.js'
8 | import path from 'path'
9 | dotnet.config()
10 |
11 | let app = express()
12 | let port = process.env.PORT
13 |
14 | // for production copy paste react js product build files in dist folder
15 | app.use(express.static('dist'))
16 |
17 | app.use(cors({ credentials: true, origin: process.env.SITE_URL , optionsSuccessStatus: 200}))
18 | app.use(cookieParser())
19 | app.use(express.json({ limit: '50mb' }))
20 |
21 | // api route
22 | app.use('/api/chat/', ChatRoute)
23 | app.use('/api/user/', UserRoute)
24 |
25 | // front end react route
26 | // app.get('/*',(req,res)=>{
27 | // res.sendFile(path.join(`${path.resolve(path.dirname(''))}/dist/index.html`))
28 | // })
29 |
30 | connectDB((err) => {
31 | if (err) return console.log("MongoDB Connect Failed : ", err)
32 |
33 | console.log("MongoDB Connected")
34 |
35 | app.listen(port, () => {
36 | console.log("server started")
37 | })
38 | })
39 |
40 |
--------------------------------------------------------------------------------
/client/src/index.scss:
--------------------------------------------------------------------------------
1 | $ColorBlack : #343541;
2 |
3 | html {
4 | scroll-behavior: smooth;
5 | }
6 |
7 | * {
8 | margin: 0;
9 | padding: 0;
10 | }
11 |
12 | body {
13 | height: 100%;
14 | overflow-y: auto;
15 | overflow-x: hidden;
16 | font-family: 'Lato', sans-serif;
17 |
18 | // scroll start;
19 |
20 | &::-webkit-scrollbar {
21 | width: 8px;
22 | }
23 |
24 | &::-webkit-scrollbar-track {
25 | background: transparent;
26 | }
27 |
28 | &:is(.light)::-webkit-scrollbar-thumb {
29 | background: rgba(217, 217, 227, .8);
30 | border-radius: 30px;
31 |
32 | &:hover {
33 | background: rgba(236, 236, 241, 1);
34 | }
35 | }
36 |
37 | &:is(.dark)::-webkit-scrollbar-thumb {
38 | background: rgba(86, 88, 105, 1);
39 | border-radius: 30px;
40 |
41 | &:hover {
42 | background: rgba(172, 172, 190, 1);
43 | }
44 | }
45 |
46 | //scroll end
47 |
48 | &:is(.light) {
49 | // body background
50 | background: white;
51 |
52 | .currentColor {
53 | color: $ColorBlack;
54 | }
55 | }
56 |
57 | &:is(.dark) {
58 | // body background
59 | background: rgba(52, 53, 65, 1);
60 |
61 | .currentColor {
62 | color: #ECECF1;
63 | }
64 | }
65 | }
66 |
67 | .main-grid {
68 | display: grid;
69 | grid-template-columns: 260px auto;
70 |
71 | @media screen and (max-width:767px) {
72 | grid-template-columns: 100%;
73 | }
74 | }
--------------------------------------------------------------------------------
/client/src/assets/upload.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const upload = () => {
4 | return (
5 |
34 | );
35 | };
36 |
37 | export default upload;
38 |
--------------------------------------------------------------------------------
/client/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import react from "@vitejs/plugin-react";
3 | import { VitePWA } from "vite-plugin-pwa";
4 |
5 | // https://vitejs.dev/config/
6 | export default defineConfig({
7 | server: {
8 | host: true,
9 | port: 80,
10 | strictPort: true,
11 | proxy: {
12 | "/api": {
13 | target: "http://localhost:5000/api",
14 | changeOrigin: true,
15 | rewrite: (path) => path.replace(/^\/api/, ""),
16 | },
17 | },
18 | },
19 | plugins: [
20 | react(),
21 |
22 | VitePWA({
23 | devOptions: {
24 | enabled: false,
25 | },
26 | manifest: {
27 | theme_color: "#fff",
28 | background_color: "#fff",
29 | display: "fullscreen",
30 | scope: "/",
31 | start_url: "/",
32 | name: "ChatGPT",
33 | short_name: "ChatGPT",
34 | description: "ChatGPT OpenAI",
35 | icons: [
36 | {
37 | src: "/manifest/icon-192x192.png",
38 | sizes: "192x192",
39 | type: "image/png",
40 | },
41 | {
42 | src: "/manifest/icon-256x256.png",
43 | sizes: "256x256",
44 | type: "image/png",
45 | },
46 | {
47 | src: "/manifest/icon-384x384.png",
48 | sizes: "384x384",
49 | type: "image/png",
50 | },
51 | {
52 | src: "/manifest/icon-512x512.png",
53 | sizes: "512x512",
54 | type: "image/png",
55 | },
56 | ],
57 | },
58 | }),
59 | ],
60 | });
61 |
--------------------------------------------------------------------------------
/client/src/assets/eyeHide.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const EyeHide = () => {
4 | return (
5 |
24 | )
25 | }
26 |
27 | export default EyeHide
28 |
--------------------------------------------------------------------------------
/client/src/assets/google.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Google = () => {
4 | return (
5 |
29 | )
30 | }
31 |
32 | export default Google
33 |
--------------------------------------------------------------------------------
/client/src/protected.jsx:
--------------------------------------------------------------------------------
1 | import React, { useLayoutEffect, useState } from "react";
2 | import { useDispatch } from "react-redux";
3 | import { Outlet, useLocation, useNavigate } from "react-router-dom";
4 | import { setLoading } from "./redux/loading";
5 | import instance from "./config/instance";
6 | import { emptyUser, insertUser } from "./redux/user";
7 | import { emptyAllRes } from "./redux/messages";
8 |
9 | const ProtectedRoute = ({ offline, authed }) => {
10 | const [component, setComponent] = useState(null);
11 |
12 | const location = useLocation();
13 | const navigate = useNavigate();
14 | const dispatch = useDispatch();
15 |
16 | useLayoutEffect(() => {
17 | dispatch(setLoading(true));
18 | const getResponse = async () => {
19 | let res = null;
20 |
21 | try {
22 | res = await instance.get("/api/user/checkLogged");
23 | if (res?.data?.data) {
24 | dispatch(insertUser(res?.data?.data));
25 | }
26 | } catch (err) {
27 | console.log(err);
28 |
29 | if (err?.response?.data?.status === 405) {
30 | dispatch(emptyUser());
31 | dispatch(emptyAllRes());
32 | if (authed) {
33 | navigate("/login");
34 | } else {
35 | setComponent();
36 | }
37 | } else if (err?.code !== "ERR_NETWORK") {
38 | navigate("/something-went-wrong");
39 | }
40 | } finally {
41 | if (res?.data?.status === 208) {
42 | if (!authed) {
43 | navigate("/");
44 | } else {
45 | setComponent();
46 | }
47 | }
48 | }
49 | };
50 |
51 | if (!offline) {
52 | getResponse();
53 | }
54 | }, [location]);
55 |
56 | return component;
57 | };
58 |
59 | export default ProtectedRoute;
60 |
--------------------------------------------------------------------------------
/server/mail/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | GE CoPilot™
9 |
10 |
11 |
13 |
14 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/client/src/assets/Profile.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Profile = ({ profilePicture }) => {
4 | return profilePicture ? (
5 |
11 | ) : (
12 |
51 | );
52 | };
53 |
54 | export default Profile;
55 |
--------------------------------------------------------------------------------
/client/src/page/login.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { useDispatch, useSelector } from "react-redux";
3 | import { useLocation, useNavigate } from "react-router-dom";
4 | import { Grant } from "../assets";
5 | import { LoginComponent } from "../components";
6 | import { setLoading } from "../redux/loading";
7 | import "./style.scss";
8 |
9 | const Login = () => {
10 | const location = useLocation();
11 |
12 | const [auth, setAuth] = useState(false);
13 |
14 | const { user } = useSelector((state) => state);
15 |
16 | const dispatch = useDispatch();
17 |
18 | const navigate = useNavigate();
19 |
20 | useEffect(() => {
21 | if (!user) {
22 | if (location?.pathname === "/login/auth") {
23 | setAuth(true);
24 | setTimeout(() => {
25 | dispatch(setLoading(false));
26 | }, 1000);
27 | } else {
28 | setAuth(false);
29 | setTimeout(() => {
30 | dispatch(setLoading(false));
31 | }, 1000);
32 | }
33 | }
34 | }, [location]);
35 |
36 | return (
37 |
38 |
39 | {auth ? (
40 |
41 | ) : (
42 |
43 |
44 |
45 |
46 |
47 |
48 |
Welcome to GE CoPilot™
49 |
Log in or Sign up with your account to continue
50 |
51 |
52 |
53 |
60 |
67 |
68 |
69 | )}
70 |
71 |
72 |
73 |
74 |
75 |
76 | );
77 | };
78 |
79 | export default Login;
80 |
--------------------------------------------------------------------------------
/client/src/components/auth/registerPendings.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { useNavigate } from 'react-router-dom'
3 | import { Grant } from '../../assets'
4 | import instance from '../../config/instance'
5 | import './style.scss'
6 |
7 | const RegisterPendings = ({ _id }) => {
8 | const navigate = useNavigate()
9 |
10 | const [formData, setFormData] = useState({
11 | fName: '',
12 | lName: ''
13 | })
14 |
15 | const formHandle = async (e) => {
16 | e.preventDefault()
17 | if (formData?.fName && formData?.lName) {
18 | let res = null
19 | try {
20 | res = await instance.put('/api/user/signup-finish', {
21 | fName: formData.fName,
22 | lName: formData.lName,
23 | _id
24 | })
25 | } catch (err) {
26 | console.log(err)
27 | if (err?.response?.data?.status === 422) {
28 | alert("Already Registered")
29 | navigate('/login')
30 | } else {
31 | alert(err)
32 | }
33 | } finally {
34 | if (res?.data?.status === 208) {
35 | navigate('/')
36 | } else if (res) {
37 | navigate('/login')
38 | }
39 | }
40 | } else {
41 | alert("Enter full name")
42 | }
43 | }
44 |
45 | return (
46 |
47 |
48 |
49 |
50 |
51 |
Tell us about you
52 |
53 |
70 |
71 |
72 | )
73 | }
74 |
75 |
76 | export default RegisterPendings
77 |
--------------------------------------------------------------------------------
/client/src/page/forgot.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { useDispatch, useSelector } from "react-redux";
3 | import { useLocation, useNavigate, useParams } from "react-router-dom";
4 | import { ForgotComponent } from "../components";
5 | import instance from "../config/instance";
6 | import { setLoading } from "../redux/loading";
7 | import "./style.scss";
8 |
9 | const Forgot = () => {
10 | const { user } = useSelector((state) => state);
11 |
12 | const { userId = null, secret = null } = useParams();
13 |
14 | const location = useLocation();
15 |
16 | const dispatch = useDispatch();
17 |
18 | const navigate = useNavigate();
19 |
20 | const [isRequest, setIsRequest] = useState(true);
21 |
22 | useEffect(() => {
23 | if (!user) {
24 | if (
25 | location?.pathname === "/forgot/" ||
26 | location?.pathname === "/forgot"
27 | ) {
28 | setIsRequest(true);
29 | setTimeout(() => {
30 | dispatch(setLoading(false));
31 | }, 1000);
32 | } else {
33 | const getResponse = async () => {
34 | let res = null;
35 |
36 | try {
37 | res = await instance.get("/api/user/forgot-check", {
38 | params: {
39 | userId,
40 | secret,
41 | },
42 | });
43 | } catch (err) {
44 | console.log(err);
45 | if (err?.response?.status === 404) {
46 | navigate("/404");
47 | } else {
48 | alert(err);
49 | navigate("/forgot");
50 | }
51 | } finally {
52 | if (res?.data?.status !== 208) {
53 | setIsRequest(false);
54 | setTimeout(() => {
55 | dispatch(setLoading(false));
56 | }, 1000);
57 | }
58 | }
59 | };
60 |
61 | getResponse();
62 | }
63 | }
64 | }, [location]);
65 |
66 | return (
67 |
68 |
69 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | );
81 | };
82 |
83 | export default Forgot;
84 |
--------------------------------------------------------------------------------
/client/src/page/signup.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { useDispatch, useSelector } from "react-redux";
3 | import { useLocation, useNavigate, useParams } from "react-router-dom";
4 | import { RegisterPendings, SignupComponent } from "../components";
5 | import instance from "../config/instance";
6 | import { setLoading } from "../redux/loading";
7 | import "./style.scss";
8 |
9 | const Signup = () => {
10 | const { user } = useSelector((state) => state);
11 |
12 | const [pending, setPending] = useState(false);
13 |
14 | const { id } = useParams();
15 |
16 | const dispatch = useDispatch();
17 |
18 | const location = useLocation();
19 |
20 | const navigate = useNavigate();
21 |
22 | useEffect(() => {
23 | if (!user) {
24 | if (
25 | location?.pathname === "/signup" ||
26 | location?.pathname === "/signup/"
27 | ) {
28 | setPending(false);
29 | setTimeout(() => {
30 | dispatch(setLoading(false));
31 | }, 1000);
32 | } else {
33 | const checkPending = async () => {
34 | let res = null;
35 | try {
36 | res = await instance.get("/api/user/checkPending", {
37 | params: {
38 | _id: id,
39 | },
40 | });
41 | } catch (err) {
42 | console.log(err);
43 | if (err?.response?.status === 404) {
44 | navigate("/404");
45 | } else {
46 | alert(err);
47 | navigate("/signup");
48 | }
49 | } finally {
50 | if (res?.data?.status !== 208) {
51 | setPending(true);
52 | setTimeout(() => {
53 | dispatch(setLoading(false));
54 | }, 1000);
55 | }
56 | }
57 | };
58 |
59 | checkPending();
60 | }
61 | }
62 | }, [location]);
63 |
64 | return (
65 |
66 |
67 | {pending ? (
68 |
69 | ) : (
70 | <>
71 |
72 |
73 |
74 |
75 |
76 | >
77 | )}
78 |
79 |
80 | );
81 | };
82 |
83 | export default Signup;
84 |
--------------------------------------------------------------------------------
/client/src/redux/messages.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from "@reduxjs/toolkit";
2 |
3 | let messagesSlice = createSlice({
4 | name: 'messages',
5 | initialState: {
6 | prompt: '',
7 | content: '',
8 | _id: null,
9 | latest: {
10 | prompt: '',
11 | content: ''
12 | },
13 | all: []
14 | },
15 | reducers: {
16 | emptyAllRes: () => {
17 | return {
18 | prompt: '',
19 | content: '',
20 | _id: null,
21 | latest: {
22 | prompt: '',
23 | content: ''
24 | },
25 | all: []
26 | }
27 | },
28 | addList: (state, { payload }) => {
29 | const { _id, items } = payload
30 | state._id = _id
31 | state.all = items
32 | return state
33 | },
34 | insertNew: (state, { payload }) => {
35 | const { chatsId, content = null,
36 | resume = false, fullContent = null,
37 | _id = null, prompt = null } = payload
38 |
39 | if (_id) {
40 | state._id = _id
41 | }
42 |
43 | state.latest.id = chatsId
44 |
45 | if (prompt) {
46 | state.latest.prompt = prompt
47 | }
48 |
49 | const addToList = (latest) => {
50 | if (state['all'].find(obj => obj.id === latest.id)) {
51 | state['all'].forEach(obj => {
52 | if (obj.id === latest.id) {
53 | obj.content = latest.content
54 | }
55 | })
56 | } else {
57 | state['all'].push(latest)
58 | }
59 | }
60 |
61 | if (content && resume) {
62 | state.latest.content += content
63 | addToList(state.latest)
64 |
65 | } else if (content) {
66 | state.latest.content = content
67 | addToList(state.latest)
68 | }
69 |
70 | if (fullContent) {
71 | state.content = fullContent
72 | }
73 |
74 | return state
75 | },
76 | livePrompt: (state, { payload }) => {
77 | state.prompt = payload
78 | return state
79 | }
80 | }
81 | })
82 |
83 | export const { emptyAllRes, insertNew, livePrompt, addList } = messagesSlice.actions
84 | export default messagesSlice.reducer
--------------------------------------------------------------------------------
/server/mail/otp.js:
--------------------------------------------------------------------------------
1 | import nodemailer from 'nodemailer';
2 | import dotenv from 'dotenv';
3 |
4 | dotenv.config();
5 |
6 | const transporter = nodemailer.createTransport({
7 | service: 'gmail',
8 | host: 'smtp.gmail.com',
9 | auth: {
10 | user: process.env.MAIL_EMAIL,
11 | pass: process.env.MAIL_SECRET
12 | }
13 | });
14 |
15 | const sendOTP = ({ to, otp }) => {
16 | const html = generateOTPTemplate(otp);
17 | const subject = 'Your OTP from GE CoPilot™';
18 |
19 | const options = {
20 | from: `GE CoPilot™ <${process.env.MAIL_EMAIL}>`,
21 | to,
22 | subject,
23 | html
24 | };
25 |
26 | transporter.sendMail(options, (err, info) => {
27 | if (err) {
28 | console.error(err);
29 | } else {
30 | console.log('Email sent: ', info.response);
31 | }
32 | });
33 | };
34 |
35 | const generateOTPTemplate = (otp) => {
36 | const template = `
37 |
38 |
39 |
40 |
41 |
42 |
43 | OTP from GE CoPilot™
44 |
45 |
46 |
47 |
48 |

49 |
Your OTP is: ${otp}
50 |
Use this OTP to proceed with your action.
51 |
52 |
53 |
54 |
55 | `;
56 |
57 | return template;
58 | };
59 |
60 | export default sendOTP;
61 |
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the public line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # public
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 |
107 | # Docusaurus cache and generated files
108 | .docusaurus
109 |
110 | # Serverless directories
111 | .serverless/
112 |
113 | # FuseBox cache
114 | .fusebox/
115 |
116 | # DynamoDB Local files
117 | .dynamodb/
118 |
119 | # TernJS port file
120 | .tern-port
121 |
122 | # Stores VSCode versions used for testing VSCode extensions
123 | .vscode-test
124 |
125 | # yarn v2
126 | .yarn/cache
127 | .yarn/unplugged
128 | .yarn/build-state.yml
129 | .yarn/install-state.gz
130 | .pnp.*
--------------------------------------------------------------------------------
/server/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the public line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # public
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 |
107 | # Docusaurus cache and generated files
108 | .docusaurus
109 |
110 | # Serverless directories
111 | .serverless/
112 |
113 | # FuseBox cache
114 | .fusebox/
115 |
116 | # DynamoDB Local files
117 | .dynamodb/
118 |
119 | # TernJS port file
120 | .tern-port
121 |
122 | # Stores VSCode versions used for testing VSCode extensions
123 | .vscode-test
124 |
125 | # yarn v2
126 | .yarn/cache
127 | .yarn/unplugged
128 | .yarn/build-state.yml
129 | .yarn/install-state.gz
130 | .pnp.*
--------------------------------------------------------------------------------
/client/src/components/content/new.jsx:
--------------------------------------------------------------------------------
1 | import React, { memo } from 'react'
2 | import { useDispatch } from 'react-redux'
3 | import { Sun, Thunder, Warning } from '../../assets'
4 | import { livePrompt } from '../../redux/messages'
5 | import './style.scss'
6 |
7 | const New = memo(() => {
8 | const dispatch = useDispatch()
9 | return (
10 |
11 |
12 |
GE CoPilot™
13 |
14 |
15 |
16 |
17 |
18 |
19 |
Examples
20 |
21 |
22 |
{
23 | dispatch(livePrompt("Explain quantum computing in simple terms"))
24 | }}>
25 |
"Explain quantum computing in simple terms" →
26 |
27 |
28 |
{
29 | dispatch(livePrompt("Got any creative ideas for a 10 year old’s birthday?"))
30 | }}>
31 |
"Got any creative ideas for a 10 year old’s birthday?" →
32 |
33 |
34 |
{
35 | dispatch(livePrompt("How do I make an HTTP request in Javascript?"))
36 | }}>
37 |
"How do I make an HTTP request in Javascript?" →
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
Capabilities
46 |
47 |
48 |
49 |
Remembers what user said earlier in the conversation
50 |
51 |
52 |
53 |
Allows user to provide follow-up corrections
54 |
55 |
56 |
57 |
Trained to decline inappropriate requests
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
Limitations
66 |
67 |
68 |
69 |
May occasionally generate incorrect information
70 |
71 |
72 |
73 |
May occasionally produce harmful instructions or biased content
74 |
75 |
76 |
77 |
Limited knowledge of world and events after 2021
78 |
79 |
80 |
81 |
82 |
83 | )
84 | })
85 |
86 | export default New
--------------------------------------------------------------------------------
/client/src/components/auth/FormFeild.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useCallback, useRef, useState } from 'react'
2 | import { Eye, EyeHide } from '../../assets'
3 |
4 | const FormFeild = ({ label,
5 | value, name, type, handleInput,
6 | passwordClass, isDisabled, error }) => {
7 |
8 | const [showPass, setShowPass] = useState(false)
9 |
10 | let inputRef = useRef()
11 | let labelRef = useRef()
12 |
13 | const inputClass = useCallback((add, label, input) => {
14 | if (add) {
15 | labelRef.current?.classList.add(...label)
16 | inputRef.current?.classList.add(...input)
17 | } else {
18 | labelRef.current?.classList.remove(...label)
19 | inputRef.current?.classList.remove(...input)
20 | }
21 | }, [])
22 |
23 | return (
24 |
25 | {label && }
28 |
29 | {
34 | inputClass(true, ["active-label", "active-label-green"], ["active-input", "active-input-green"])
35 | }}
36 |
37 | onBlur={() => {
38 | if (inputRef.current?.value.length <= 0) {
39 | inputClass(false, ["active-label", "active-label-green"], ["active-input", "active-input-green"])
40 | } else {
41 | inputClass(false, ["active-label-green"], ["active-input-green"])
42 | }
43 | }}
44 |
45 | onInput={(e) => {
46 | handleInput(e)
47 | if (passwordClass) {
48 | document.querySelector('#alertBox').style.display = "block"
49 |
50 | if (e.target.value.length >= 8) {
51 | passwordClass('#passAlertError', "#passAlertDone")
52 | } else {
53 | passwordClass("#passAlertDone", "#passAlertError")
54 | }
55 | }
56 | }}
57 |
58 | disabled={isDisabled} readOnly={isDisabled} required
59 | />
60 |
61 | {
62 | type === 'password' && <>
63 | {showPass ?
67 | : }
71 | >
72 | }
73 |
74 | )
75 | }
76 |
77 | export default FormFeild
78 |
--------------------------------------------------------------------------------
/client/src/components/content/chat.jsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | forwardRef,
3 | Fragment,
4 | useImperativeHandle, useRef
5 | } from 'react'
6 | import { useDispatch, useSelector } from 'react-redux'
7 | import { Grant } from '../../assets'
8 | import { insertNew } from '../../redux/messages'
9 | import './style.scss'
10 |
11 | const Chat = forwardRef(({ error }, ref) => {
12 |
13 | const dispatch = useDispatch()
14 |
15 | const contentRef = useRef()
16 |
17 | const { user, messages } = useSelector((state) => state)
18 | const { latest, content, all } = messages
19 |
20 | const loadResponse = (stateAction,
21 | response = content,
22 | chatsId = latest?.id) => {
23 |
24 | clearInterval(window.interval)
25 |
26 | stateAction({ type: 'resume', status: true })
27 |
28 | contentRef?.current?.classList?.add("blink")
29 |
30 | // let index = 0
31 | contentRef.current.innerHTML = response
32 | // window.interval = setInterval(() => {
33 | // if (index < response.length && contentRef?.current) {
34 | // if (index === 0) {
35 | dispatch(insertNew({ chatsId, content: response }))
36 | contentRef.current.innerHTML = response
37 | // } else {
38 | // dispatch(insertNew({ chatsId, content: response.charAt(index), resume: true }))
39 | // contentRef.current.innerHTML += response.charAt(index)
40 | // }
41 | // index++
42 | // } else {
43 | stopResponse(stateAction)
44 | }
45 | // }, 20)
46 |
47 | // }
48 |
49 | const stopResponse = (stateAction) => {
50 | if (contentRef?.current) {
51 | contentRef.current.classList.remove('blink')
52 | }
53 | stateAction({ type: 'resume', status: false })
54 | clearInterval(window.interval)
55 | }
56 |
57 | useImperativeHandle(ref, () => ({
58 | stopResponse,
59 | loadResponse,
60 | clearResponse: () => {
61 | if (contentRef?.current) {
62 | contentRef.current.innerHTML = ''
63 | contentRef?.current?.classList.add("blink")
64 | }
65 | }
66 | }))
67 |
68 | return (
69 |
70 | {
71 | all?.filter((obj) => {
72 | //console.log(all)
73 | return !obj.id ? true : obj?.id !== latest?.id
74 | })?.map((obj, key) => {
75 | //console.log(obj)
76 | return (
77 |
78 |
79 |
80 | {user?.fName?.charAt(0)}
81 |
82 |
83 | {obj?.prompt}
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | {obj?.content}
94 |
95 |
96 |
97 |
98 | )
99 | })
100 | }
101 |
102 | {
103 | latest?.prompt?.length > 0 && (
104 |
105 |
106 |
107 | {user?.fName?.charAt(0)}
108 |
109 |
110 | {latest?.prompt}
111 |
112 |
113 |
114 |
115 |
116 |
117 | {error && !}
118 |
119 |
120 | {
121 | error ?
122 | Something went wrong. If this issue persists please contact us through our help center at help.openai.com.
123 |
:
124 | }
125 |
126 |
127 |
128 | )
129 | }
130 |
131 | )
132 | })
133 | export default Chat
--------------------------------------------------------------------------------
/client/src/assets/gptIcon.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const GptIcon = () => {
4 | return (
5 |
6 | )
7 | }
8 |
9 | export default GptIcon
10 |
--------------------------------------------------------------------------------
/client/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { createContext, useEffect, useLayoutEffect, useState } from "react";
2 | import { Menu } from "./components";
3 | import { Routes, Route } from "react-router-dom";
4 | import { Error, Forgot, Login, Main, Signup } from "./page";
5 | import { useSelector } from "react-redux";
6 | import ProtectedRoute from "./protected";
7 | import Loading from "./components/loading/loading";
8 | import instance from "./config/instance";
9 |
10 | export const documentsContext = createContext({
11 | documents: [],
12 | setDocuments: () => {},
13 | getFiles: () => {},
14 | });
15 |
16 | const App = () => {
17 | const [offline, setOffline] = useState(!window.navigator.onLine);
18 | const [file_id, set_file_id] = useState(null);
19 | const { loading, user } = useSelector((state) => state);
20 | const [documents, setDocuments] = useState([]);
21 | const { _id } = useSelector((state) => state.messages);
22 | const changeColorMode = (to) => {
23 | if (to) {
24 | localStorage.setItem("darkMode", true);
25 |
26 | document.body.className = "dark";
27 | } else {
28 | localStorage.removeItem("darkMode");
29 |
30 | document.body.className = "light";
31 | }
32 | };
33 |
34 | const getFiles = async () => {
35 | let res = null;
36 | if (!_id) return console.log("No chat id");
37 | else {
38 | try {
39 | res = await instance.get("/api/chat/upload?chatId=" + _id);
40 | } catch (err) {
41 | console.log(err);
42 | } finally {
43 | if (res?.data) {
44 | console.log(res.data);
45 | setDocuments(res?.data?.data);
46 | }
47 | }
48 | }
49 | };
50 | // Dark & Light Mode
51 | useLayoutEffect(() => {
52 | let mode = localStorage.getItem("darkMode");
53 |
54 | if (mode) {
55 | changeColorMode(true);
56 | } else {
57 | changeColorMode(false);
58 | }
59 | });
60 |
61 | // Offline
62 | useEffect(() => {
63 | window.addEventListener("online", (e) => {
64 | location.reload();
65 | });
66 |
67 | window.addEventListener("offline", (e) => {
68 | setOffline(true);
69 | });
70 | });
71 |
72 | return (
73 |
74 |
75 | {user && (
76 |
77 |
82 |
83 | )}
84 |
85 | {loading && }
86 |
87 | {offline && (
88 |
92 | )}
93 |
94 |
95 | }>
96 |
104 | }
105 | />
106 |
113 | }
114 | />
115 |
122 | }
123 | />
124 |
125 |
126 | }>
127 | } />
128 | } />
129 | } />
130 | } />
131 | } />
132 | } />
133 |
134 |
138 | }
139 | />
140 |
141 |
142 |
143 | );
144 | };
145 |
146 | export default App;
147 |
148 |
--------------------------------------------------------------------------------
/client/src/components/auth/signup.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useCallback, useReducer, useState } from 'react'
2 | import { Grant, Tick, Google, Microsoft, Mail, } from '../../assets'
3 | import { Link, useNavigate } from 'react-router-dom'
4 | import { useGoogleLogin } from '@react-oauth/google'
5 | import FormFeild from './FormFeild'
6 | import axios from 'axios'
7 | import instance from '../../config/instance'
8 | import './style.scss'
9 |
10 | const reducer = (state, { type, status }) => {
11 | switch (type) {
12 | case 'filled':
13 | return { filled: status }
14 | case 'error':
15 | return { error: status, filled: state.filled }
16 | case 'mail':
17 | return { mail: status, error: !status }
18 | default: return state
19 | }
20 | }
21 |
22 | const SignupComponent = () => {
23 |
24 | const navigate = useNavigate()
25 |
26 | const [state, stateAction] = useReducer(reducer, {
27 | filled: false,
28 | error: false
29 | })
30 |
31 | const [formData, setFormData] = useState({
32 | email: '',
33 | pass: '',
34 | manual: false
35 | })
36 |
37 | const handleInput = (e) => {
38 | setFormData({
39 | ...formData,
40 | [e.target.name]: e.target.value
41 | })
42 | }
43 |
44 | const formHandle = async (e) => {
45 | e?.preventDefault()
46 | if (formData?.pass.length >= 8 && (formData?.email.endsWith("@protondatalabs.com") || formData?.email.endsWith("@grantengine.com"))) {
47 | let res = null
48 | try {
49 | res = await instance.post('/api/user/signup', formData)
50 | } catch (err) {
51 | console.log(err)
52 | if (err?.response?.data.message?.exists) {
53 | stateAction({ type: 'error', status: true })
54 | } else {
55 | stateAction({ type: 'error', status: false })
56 | }
57 |
58 | } finally {
59 | if (res?.data?.status === 208) {
60 | navigate('/')
61 | } else if (res?.['data']?.data?.manual) {
62 | stateAction({ type: 'mail', status: true })
63 | } else if (res) {
64 | stateAction({ type: 'error', status: false })
65 | if (res['data']?.data?._id) navigate(`/signup/pending/${res?.['data']?.data._id}`)
66 | }
67 | }
68 | } else {
69 | alert("Make sure you are using Grant Engine's Email and your password is greater that 8 characters")
70 | }
71 | }
72 |
73 | const googleAuth = useGoogleLogin({
74 | onSuccess: async response => {
75 | let res = null
76 | try {
77 | res = await axios.get("https://www.googleapis.com/oauth2/v3/userinfo", {
78 | headers: {
79 | "Authorization": `Bearer ${response.access_token}`
80 | }
81 | })
82 | } catch (err) {
83 | console.log(err)
84 | } finally {
85 | if (res?.data?.email_verified) {
86 | setFormData({
87 | ...formData,
88 | manual: false,
89 | email: res.data.email,
90 | token: response.access_token
91 | })
92 |
93 | stateAction({ type: 'filled', status: true })
94 | }
95 | }
96 |
97 | }
98 | })
99 |
100 | const passwordClass = useCallback((remove, add) => {
101 | document.querySelector(remove).classList?.remove('active')
102 | document.querySelector(add).classList?.add('active')
103 | }, [])
104 |
105 | return (
106 |
107 |
108 |
109 |
110 |
111 | {
112 | !state.mail ? (
113 |
114 |
115 |
Create your account
116 |
117 |
Please note that phone verification is required for signup. Your number will only be used to verify your identity for security purposes.
118 |
119 |
120 |
121 | {
122 | !state.filled ? (
123 |
124 |
143 |
144 |
145 | Already have an account?
146 | Log in
147 |
148 |
149 |
150 |
151 | OR
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 | ) : (
161 |
217 | )
218 | }
219 |
220 | ) : (
221 |
222 |
223 |
224 |
225 |
226 |
227 |
Check Your Email
228 |
229 |
230 |
231 |
Please check the email address {formData.email} for instructions to signup.
232 |
233 |
234 |
235 |
236 | )
237 | }
238 |
239 | )
240 | }
241 |
242 | export default SignupComponent
243 |
--------------------------------------------------------------------------------
/client/src/components/auth/login.jsx:
--------------------------------------------------------------------------------
1 | import React, { useReducer, useState } from "react";
2 | import { Grant, Google, Microsoft } from "../../assets";
3 | import { Link, useNavigate } from "react-router-dom";
4 | import FormFeild from "./FormFeild";
5 | import { useGoogleLogin } from "@react-oauth/google";
6 | import { useDispatch } from "react-redux";
7 | import { insertUser } from "../../redux/user";
8 | import instance from "../../config/instance";
9 | import "./style.scss";
10 |
11 | const reducer = (state, { type, status }) => {
12 | switch (type) {
13 | case "filled":
14 | return { filled: status };
15 | case "error":
16 | return { error: status, filled: state.filled };
17 | default:
18 | return state;
19 | }
20 | };
21 |
22 | const LoginComponent = () => {
23 | const dispatch = useDispatch();
24 | const navigate = useNavigate();
25 | const [email, setEmail] = useState(false);
26 | const [password, setPassword] = useState(false);
27 | const [otp, setOtp] = useState("");
28 | const [state, stateAction] = useReducer(reducer, {
29 | filled: false,
30 | password: false,
31 | error: false,
32 | });
33 |
34 | const [formData, setFormData] = useState({
35 | email: "",
36 | pass: "",
37 | otp: "",
38 | manual: true,
39 | });
40 |
41 | const handleInput = (e) => {
42 | setFormData({
43 | ...formData,
44 | [e.target.name]: e.target.value,
45 | });
46 | };
47 |
48 | const googleAuth = useGoogleLogin({
49 | onSuccess: (response) => {
50 | formHandle(null, {
51 | manual: false,
52 | token: response.access_token,
53 | });
54 | },
55 | });
56 |
57 | const otpHandle = async (e) => {
58 | e.preventDefault();
59 | const given_otp = e.target.otp.value;
60 | try {
61 | console.log(formData.email, given_otp)
62 | const otp_res = await instance.post("/api/user/verify_otp", {
63 | email: formData.email,
64 | otp: given_otp,
65 | });
66 | console.log(otp_res.data);
67 | if (otp_res.status === 200 && otp_res.data.message === "Success") {
68 | dispatch(insertUser(otp_res.data.data));
69 | navigate("/chat");
70 | }
71 | } catch (err) {
72 | if (err.response.status === 422) {
73 | stateAction({ type: "error", status: true });
74 | alert("Invalid OTP")
75 | }
76 | console.log(err);
77 | }
78 | };
79 |
80 | const formHandle = async (e, googleData) => {
81 | e?.preventDefault();
82 | let res = null;
83 | try {
84 | res = await instance.get("/api/user/login", {
85 | params: googleData || { email: formData.email, pass: formData.pass },
86 | });
87 | console.log(googleData);
88 | } catch (err) {
89 | console.log(err);
90 | if (err?.response?.data?.status === 422) {
91 | stateAction({ type: "error", status: true });
92 | }
93 | } finally {
94 | if (res?.data?.message === "Success" && res?.data?.status === 200) {
95 | console.log(res.data);
96 | // stateAction({ type: "error", status: false });
97 | const user_otp = Math.floor(1000 + Math.random() * 9000);
98 | setPassword(true);
99 | setOtp(user_otp);
100 | const otp_res = await instance.post("/api/user/send_otp", {
101 | email: formData.email,
102 | otp: user_otp,
103 | });
104 |
105 | console.log(user_otp);
106 | console.log(otp_res.data)
107 | // dispatch(insertUser(res.data.data));
108 | // navigate("/two_step_verification");
109 | }
110 | }
111 | };
112 |
113 | return (
114 |
115 |
116 |
117 |
118 |
119 |
120 | {!state.filled ?
Welcome back
: Enter your password
}
121 |
122 |
123 | {!email && !password ? (
124 |
131 | ) : email && !password ? (
132 |
139 | ) : (
140 |
146 | )}
147 |
148 | );
149 | };
150 |
151 | const LoginEmail = ({
152 | formData,
153 | handleInput,
154 | googleAuth,
155 | stateAction,
156 | setEmail,
157 | }) => {
158 | return (
159 |
160 |
181 |
182 |
183 | Don't have an account?
184 | Sign up
185 |
186 |
187 |
188 |
189 | OR
190 |
191 |
192 |
193 |
196 |
197 |
198 |
199 | );
200 | };
201 |
202 | const LoginEmailPassword = ({
203 | formHandle,
204 | stateAction,
205 | formData,
206 | handleInput,
207 | state,
208 | }) => {
209 | return (
210 |
260 | );
261 | };
262 |
263 | const Two_Step_Auth = ({ formData, handleInput, otpHandle, state }) => {
264 | return (
265 |
266 |
Two Step Verification
267 |
286 |
287 | );
288 | };
289 |
290 | export default LoginComponent;
291 |
--------------------------------------------------------------------------------
/client/src/components/content/style.scss:
--------------------------------------------------------------------------------
1 | @import '../../index.scss';
2 |
3 | @keyframes txtBlink {
4 | 0% {
5 | border-right: 4px solid;
6 | }
7 |
8 | 50% {
9 | border-right: none;
10 | }
11 |
12 | 100% {
13 | border-right: 4px solid;
14 | }
15 | }
16 |
17 | .light {
18 | .New {
19 | .card-bg {
20 | background: rgba(247, 247, 248, 1);
21 | border-radius: .375rem;
22 | padding: .75rem;
23 |
24 | p {
25 | font-size: 14px;
26 | }
27 |
28 | &:is(.hover):hover {
29 | background: rgba(236, 236, 241, 1);
30 | cursor: pointer;
31 | }
32 | }
33 | }
34 |
35 | .Chat {
36 | .qs {
37 | background: white;
38 |
39 | .txt {
40 | color: #343541;
41 | }
42 | }
43 |
44 | .res {
45 | border-top: 1px solid #d9d9e3;
46 | border-bottom: 1px solid #d9d9e3;
47 | background: rgba(247, 247, 248, 1);
48 |
49 | .txt {
50 | .error {
51 | color: #374151;
52 | }
53 |
54 | span {
55 | color: #374151;
56 |
57 | &span::after {
58 | border-color: #374151;
59 | }
60 | }
61 | }
62 | }
63 | }
64 | }
65 |
66 | .dark {
67 | .New {
68 | .card-bg {
69 | background: hsla(0, 0%, 100%, .05);
70 | border-radius: .375rem;
71 | padding: .75rem;
72 |
73 | p {
74 | font-size: 14px;
75 | }
76 |
77 | &:is(.hover):hover {
78 | background: rgba(32, 33, 35, 1);
79 | cursor: pointer;
80 | }
81 | }
82 | }
83 |
84 | .Chat {
85 | .qs {
86 | background: rgba(52, 53, 65, 1);
87 |
88 | .txt {
89 | color: #ECECF1;
90 | }
91 | }
92 |
93 | .res {
94 | background: #444654;
95 | border-top: 1px solid rgba(32, 33, 35, .5);
96 | border-bottom: 1px solid rgba(32, 33, 35, .5);
97 |
98 | .txt {
99 | .error {
100 | color: #D1D5DB;
101 | }
102 |
103 | span {
104 | color: #D1D5DB;
105 |
106 | &::after {
107 | border-color: #D1D5DB;
108 | }
109 | }
110 | }
111 | }
112 | }
113 | }
114 |
115 | .New {
116 | display: block;
117 | padding-left: 12em;
118 | padding-right: 12em;
119 | padding-top: 7em;
120 | padding-bottom: 12rem;
121 |
122 | @media only screen and (max-width: 1300px) {
123 | padding-left: 5em;
124 | padding-right: 5em;
125 | }
126 |
127 | @media only screen and (max-width: 1000px) {
128 | padding-left: 1.5em;
129 | padding-right: 1.5em;
130 | }
131 |
132 | .title {
133 | font-size: 2.25rem;
134 | font-weight: 600;
135 | margin-bottom: 2em;
136 | }
137 |
138 | .flex {
139 | display: flex;
140 | flex-direction: row;
141 | gap: .875rem;
142 |
143 | @media screen and (max-width:767px) {
144 | flex-direction: column;
145 | }
146 |
147 | svg {
148 | width: 24px;
149 | height: 24px;
150 | }
151 |
152 | .inner {
153 | flex-basis: 100%;
154 | display: flex;
155 | flex-direction: column;
156 |
157 | @media screen and (max-width:767px) {
158 | flex-basis: 100%;
159 | }
160 |
161 | .card {
162 | margin-bottom: 1em;
163 |
164 | h4 {
165 | font-weight: 400;
166 | font-size: 1.125rem;
167 | line-height: 1.75rem;
168 | }
169 | }
170 | }
171 |
172 | }
173 | }
174 |
175 | .Chat {
176 | display: block;
177 | text-align: left;
178 | padding-bottom: 12rem;
179 |
180 | .qs {
181 | padding-top: 1.5rem;
182 | padding-bottom: 1.5rem;
183 | padding-left: 10em;
184 | padding-right: 12em;
185 | display: grid;
186 | grid-template-columns: calc(30px + 1.5rem) auto;
187 |
188 | @media only screen and (max-width: 1300px) {
189 | padding-left: 5em;
190 | padding-right: 5em;
191 | }
192 |
193 | @media only screen and (max-width: 1000px) {
194 | padding-left: 1em;
195 | padding-right: 1em;
196 | }
197 |
198 | @media only screen and (max-width: 767px) {
199 | padding-top: 4rem;
200 | grid-template-columns: calc(30px + 1rem) auto;
201 | }
202 |
203 | .acc {
204 | display: flex;
205 | width: 30px;
206 | height: 30px;
207 | border: none;
208 | background: green;
209 | color: white;
210 | justify-content: center;
211 | align-items: center;
212 | border-radius: 2px;
213 | font-weight: 500;
214 | margin-right: 1.5rem;
215 |
216 | @media only screen and (max-width: 767px) {
217 | margin-right: 1rem;
218 | }
219 | }
220 |
221 | .txt {
222 | line-height: 1.5rem;
223 | font-size: 1rem;
224 | }
225 | }
226 |
227 | .res {
228 | padding-top: 1.5rem;
229 | padding-bottom: 1.5rem;
230 | padding-left: 10em;
231 | padding-right: 12em;
232 | display: grid;
233 | grid-template-columns: calc(30px + 1.5rem) auto;
234 |
235 | @media only screen and (max-width: 1300px) {
236 | padding-left: 5em;
237 | padding-right: 5em;
238 | }
239 |
240 | @media only screen and (max-width: 1000px) {
241 | padding-left: 1em;
242 | padding-right: 1em;
243 | }
244 |
245 | @media only screen and (max-width: 767px) {
246 | grid-template-columns: calc(30px + 1rem) auto;
247 | }
248 |
249 | .icon {
250 | position: relative;
251 | display: flex;
252 | width: 30px;
253 | height: 30px;
254 | border: none;
255 | background: #191949;
256 | justify-content: center;
257 | align-items: center;
258 | border-radius: 2px;
259 | font-weight: 500;
260 | margin-right: 1.5rem;
261 |
262 | @media only screen and (max-width: 767px) {
263 | margin-right: 1rem;
264 | }
265 |
266 | svg {
267 | width: 1.5rem;
268 | height: 1.5rem;
269 | color: white;
270 | }
271 |
272 | span {
273 | position: absolute;
274 | background: rgba(239, 68, 68, 1);
275 | border-radius: 50%;
276 | font-size: 10px;
277 | width: 1rem;
278 | height: 1rem;
279 | display: flex;
280 | align-items: center;
281 | justify-content: center;
282 | right: -4px;
283 | bottom: -4px;
284 | color: rgba(255,255,255,1);
285 | border: 1px solid rgba(255,255,255,1);
286 | }
287 | }
288 |
289 | .txt {
290 | display: block;
291 |
292 | .error {
293 | background: rgba(239, 68, 68, .1);
294 | border: 1px solid rgba(239, 68, 68, 1);
295 | border-radius: .375rem;
296 | padding: 0.75rem;
297 | font-size: .875rem;
298 | line-height: 1.25rem;
299 | }
300 |
301 | span {
302 | line-height: 1.5rem;
303 | font-size: 1rem;
304 | white-space: pre-wrap;
305 |
306 | &:is(.blink) {
307 | &::after {
308 | content: '';
309 | padding-left: 3px;
310 | border-right: 4px solid;
311 | animation: txtBlink 1s ease-in-out infinite;
312 | }
313 | }
314 | }
315 | }
316 | }
317 | }
--------------------------------------------------------------------------------
/client/src/components/auth/forgot.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useCallback, useReducer, useState } from 'react'
2 | import { Grant, Tick, Mail } from '../../assets'
3 | import { useNavigate } from 'react-router-dom'
4 | import FormFeild from './FormFeild'
5 | import instance from '../../config/instance'
6 | import './style.scss'
7 |
8 | const reducer = (state, { type, status }) => {
9 | switch (type) {
10 | case 'mail':
11 | return { mail: status }
12 | case 'error':
13 | return { error: status }
14 | default: return state
15 | }
16 | }
17 |
18 | const ForgotComponent = ({ isRequest, userId, secret }) => {
19 | const [state, stateAction] = useReducer(reducer, {
20 | mail: false
21 | })
22 |
23 | const navigate = useNavigate()
24 |
25 | const passwordClass = useCallback((remove, add) => {
26 | document.querySelector(remove).classList?.remove('active')
27 | document.querySelector(add).classList?.add('active')
28 | }, [])
29 |
30 | const [formData, setFormData] = useState({
31 | email: '',
32 | newPass: '',
33 | reEnter: ''
34 | })
35 |
36 | const handleInput = (e) => {
37 | setFormData({
38 | ...formData,
39 | [e.target.name]: e.target.value
40 | })
41 | }
42 |
43 | const formHandleMail = async (e) => {
44 | if (e) { e.preventDefault() }
45 | let res = null
46 | try {
47 | res = await instance.post('/api/user/forgot-request', {
48 | email: formData.email
49 | })
50 | } catch (err) {
51 | console.log(err)
52 | if (err?.response?.data?.status === 422) {
53 | stateAction({ type: 'error', status: true })
54 | }
55 | } finally {
56 | if (res?.data?.status === 208) {
57 | navigate('/')
58 | } else if (res) {
59 | stateAction({ type: 'mail', status: true })
60 | }
61 | }
62 | }
63 |
64 | const formHandleReset = async (e) => {
65 | e.preventDefault()
66 | if (userId && secret && formData?.newPass.length >= 8) {
67 | if (formData?.newPass === formData?.reEnter) {
68 | stateAction({ type: 'error', status: false })
69 |
70 | let res = null
71 | try {
72 | res = await instance.put('/api/user/forgot-finish', {
73 | userId,
74 | secret,
75 | newPass: formData.newPass,
76 | reEnter: formData.reEnter
77 | })
78 | } catch (err) {
79 | console.log(err)
80 | alert(err)
81 | navigate('/forgot')
82 | } finally {
83 | if (res) {
84 | navigate('/login')
85 | }
86 | }
87 | } else {
88 | stateAction({ type: 'error', status: true })
89 | }
90 | }
91 | }
92 |
93 | return (
94 |
95 |
96 |
97 |
98 |
99 | {
100 | isRequest ? (
101 |
102 | {
103 | !state.mail ? (
104 |
105 |
106 |
Reset your password
107 |
108 |
Enter your email address and we will send you instructions to reset your password.
109 |
110 |
111 |
136 |
137 | )
138 | : (
139 |
140 |
141 |
142 |
143 |
144 |
145 |
Check Your Email
146 |
147 |
148 |
149 |
Please check the email address {formData?.email} for instructions to reset your password.
150 |
151 |
152 |
153 |
154 | )
155 | }
156 |
157 | ) : (
158 |
159 |
160 |
Enter your password
161 |
Enter a new password below to change your password.
162 |
163 |
213 |
214 | )
215 | }
216 |
217 |
218 | )
219 | }
220 |
221 | export default ForgotComponent
222 |
--------------------------------------------------------------------------------
/client/public/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/assets/grant.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Grant = () => {
4 | return (
5 |
105 | )
106 | }
107 |
108 | export default Grant
109 |
--------------------------------------------------------------------------------
/server/helpers/chat.js:
--------------------------------------------------------------------------------
1 | import { db } from "../db/connection.js";
2 | import collections from "../db/collections.js";
3 | import { ObjectId } from "mongodb";
4 | import OpenAI from "openai";
5 |
6 | export default {
7 | newResponse: (prompt, { openai }, userId) => {
8 | return new Promise(async (resolve, reject) => {
9 | let chatId = new ObjectId().toHexString();
10 | let res = null;
11 | try {
12 | await db
13 | .collection(collections.CHAT)
14 | .createIndex({ user: 1 }, { unique: true });
15 | let dataObj = {
16 | chatId,
17 | chats: [
18 | {
19 | role: "user",
20 | content: prompt,
21 | },
22 | {
23 | role: "assistant",
24 | content: openai,
25 | },
26 | ],
27 | chat: [
28 | {
29 | prompt: prompt,
30 | content: openai,
31 | },
32 | ],
33 | };
34 |
35 | res = await db.collection(collections.CHAT).insertOne({
36 | user: userId.toString(),
37 | data: [dataObj],
38 | });
39 | } catch (err) {
40 | if (err?.code === 11000) {
41 | let updateQuery = {
42 | user: userId.toString(),
43 | };
44 | let pushQuery = {
45 | $push: {
46 | data: {
47 | chatId,
48 | chats: [
49 | {
50 | role: "user",
51 | content: prompt,
52 | },
53 | {
54 | role: "assistant",
55 | content: openai,
56 | },
57 | ],
58 | chat: [
59 | {
60 | prompt: prompt,
61 | content: openai,
62 | },
63 | ],
64 | },
65 | },
66 | };
67 |
68 | res = await db
69 | .collection(collections.CHAT)
70 | .updateOne(updateQuery, pushQuery);
71 | } else {
72 | reject(err);
73 | }
74 | } finally {
75 | if (res) {
76 | res.chatId = chatId;
77 | resolve(res);
78 | } else {
79 | reject({ text: "DB gets something wrong" });
80 | }
81 | }
82 | });
83 | },
84 | Response: (prompt, { openai }, userId, chatId, assistant_id, file_name) => {
85 | return new Promise(async (resolve, reject) => {
86 | let res = null;
87 | try {
88 | let updateObj = {
89 | $push: {
90 | "data.$.chats": {
91 | $each: [
92 | { role: "user", content: prompt },
93 | { role: "assistant", content: openai },
94 | ],
95 | },
96 | "data.$.chat": {
97 | prompt: prompt,
98 | content: openai,
99 | },
100 | },
101 | };
102 | // If file_name is not empty and not already present in the array, push it
103 | if (file_name && file_name.trim() !== "") {
104 | updateObj.$addToSet = {
105 | "data.$.file_name": file_name,
106 | };
107 | }
108 | // If assistant_id is null, set it to the incoming assistant_id
109 | if (assistant_id !== null) {
110 | updateObj.$set = {
111 | "data.$.assistant_id": assistant_id,
112 | };
113 | }
114 |
115 | // Execute the update operation
116 | res = await db.collection(collections.CHAT).updateOne(
117 | {
118 | user: userId.toString(),
119 | "data.chatId": chatId,
120 | },
121 | updateObj
122 | );
123 | } catch (err) {
124 | reject(err);
125 | } finally {
126 | if (res) {
127 | res.chatId = chatId;
128 | resolve(res);
129 | } else {
130 | reject({ text: "DB gets something wrong" });
131 | }
132 | }
133 | });
134 | },
135 |
136 | updateChat: (chatId, prompt, { openai }, userId) => {
137 | return new Promise(async (resolve, reject) => {
138 | let res = await db
139 | .collection(collections.CHAT)
140 | .updateOne(
141 | {
142 | user: userId.toString(),
143 | "data.chatId": chatId,
144 | },
145 | {
146 | $push: {
147 | data: {
148 | chatId,
149 | chats: [
150 | {
151 | role: "user",
152 | content: prompt,
153 | },
154 | {
155 | role: "assistant",
156 | content: openai,
157 | },
158 | ],
159 | },
160 | },
161 | }
162 | )
163 | .catch((err) => {
164 | reject(err);
165 | });
166 |
167 | if (res) {
168 | resolve(res);
169 | } else {
170 | reject({ text: "DB gets something wrong" });
171 | }
172 | });
173 | },
174 | getChat: (userId, chatId) => {
175 | return new Promise(async (resolve, reject) => {
176 | let res = await db
177 | .collection(collections.CHAT)
178 | .aggregate([
179 | {
180 | $match: {
181 | user: userId.toString(),
182 | },
183 | },
184 | {
185 | $unwind: "$data",
186 | },
187 | {
188 | $match: {
189 | "data.chatId": chatId,
190 | },
191 | },
192 | {
193 | $project: {
194 | _id: 0,
195 | chat: "$data.chat",
196 | },
197 | },
198 | ])
199 | .toArray()
200 | .catch((err) => [reject(err)]);
201 |
202 | if (res && Array.isArray(res) && res[0]?.chat) {
203 | resolve(res[0].chat);
204 | } else {
205 | reject({ status: 404 });
206 | }
207 | });
208 | },
209 | getHistory: (userId) => {
210 | return new Promise(async (resolve, reject) => {
211 | let res = await db
212 | .collection(collections.CHAT)
213 | .aggregate([
214 | {
215 | $match: {
216 | user: userId.toString(),
217 | },
218 | },
219 | {
220 | $unwind: "$data",
221 | },
222 | {
223 | $project: {
224 | _id: 0,
225 | chatId: "$data.chatId",
226 | chat: "$data.chat", // Project the entire 'chats' array
227 | },
228 | },
229 | ])
230 | .toArray()
231 | .catch((err) => {
232 | reject(err);
233 | });
234 |
235 | if (Array.isArray(res)) {
236 | resolve(res);
237 | } else {
238 | reject({ text: "DB Getting Some Error" });
239 | }
240 | });
241 | },
242 | deleteAllChat: (userId) => {
243 | return new Promise((resolve, reject) => {
244 | db.collection(collections.CHAT)
245 | .deleteOne({
246 | user: userId.toString(),
247 | })
248 | .then((res) => {
249 | if (res?.deletedCount > 0) {
250 | resolve(res);
251 | } else {
252 | reject({ text: "DB Getting Some Error" });
253 | }
254 | })
255 | .catch((err) => {
256 | reject(err);
257 | });
258 | });
259 | },
260 |
261 | //Get all message for OpenAI History
262 | Messages: (userId, chatId) => {
263 | return new Promise(async (resolve, reject) => {
264 | let res = await db
265 | .collection(collections.CHAT)
266 | .aggregate([
267 | {
268 | $match: {
269 | user: userId.toString(),
270 | },
271 | },
272 | {
273 | $unwind: "$data",
274 | },
275 | {
276 | $match: {
277 | "data.chatId": chatId,
278 | },
279 | },
280 | {
281 | $project: {
282 | _id: 0,
283 | chats: "$data.chats", // Project the entire 'chats' array
284 | },
285 | },
286 | ])
287 | .toArray()
288 | .catch((err) => {
289 | reject(err);
290 | });
291 |
292 | if (Array.isArray(res)) {
293 | resolve(res);
294 | } else {
295 | reject({ text: "DB Getting Some Error" });
296 | }
297 | });
298 | },
299 | //Get all file name
300 | getFiles: (userId, chatId) => {
301 | return new Promise(async (resolve, reject) => {
302 | let res = await db
303 | .collection(collections.CHAT)
304 | .aggregate([
305 | {
306 | $match: {
307 | user: userId,
308 | },
309 | },
310 | {
311 | $unwind: "$data",
312 | },
313 | {
314 | $match: {
315 | "data.chatId": chatId,
316 | },
317 | },
318 | {
319 | $project: {
320 | _id: 0,
321 | file_name: "$data.file_name", // Project the entire 'FileName' array
322 | },
323 | },
324 | ])
325 | .toArray()
326 | .catch((err) => {
327 | reject(err);
328 | });
329 |
330 | if (Array.isArray(res)) {
331 | resolve(res);
332 | } else {
333 | reject({ text: "DB Getting Some Error" });
334 | }
335 | });
336 | },
337 | deleteFile: (userId, chatId, file_name, file_id) => {
338 | const client = new OpenAI({
339 | apiKey: "api -key",
340 | });
341 | return new Promise(async (resolve, reject) => {
342 | try {
343 | const result = await db.collection(collections.CHAT).updateOne(
344 | {
345 | user: userId.toString(),
346 | "data.chatId": chatId,
347 | },
348 | {
349 | $pull: {
350 | "data.$.file_name": file_name,
351 | "data.$.files": file_id,
352 | },
353 | }
354 | );
355 | const files_data = await db.collection(collections.CHAT).aggregate([
356 | {
357 | $match: {
358 | user: userId.toString(),
359 | },
360 | },
361 | {
362 | $unwind: "$data",
363 | },
364 | {
365 | $match: {
366 | "data.chatId": chatId,
367 | },
368 | },
369 | {
370 | $project: {
371 | _id: 0,
372 | file_id: "$data.files",
373 | },
374 | },
375 | ]).toArray();
376 | let assistant = null;
377 | if (files_data[0]?.file_id?.length === 0) {
378 | assistant = {
379 | id: null
380 | }
381 | } else {
382 | assistant = await client.beta.assistants.create({
383 | name: "GE CoPilot",
384 | instructions:
385 | "You are a helpful and that answers what is asked. Retrieve the relevant information from the files.",
386 | tools: [{ type: "retrieval" }, { type: "code_interpreter" }],
387 | model: "gpt-4-0125-preview",
388 | file_ids: files_data[0]?.file_id,
389 | });
390 | }
391 | const result_chat_update = await db
392 | .collection(collections.CHAT)
393 | .updateOne(
394 | {
395 | user: userId.toString(),
396 | "data.chatId": chatId,
397 | },
398 | {
399 | $set: {
400 | "data.$.assistant_id": assistant.id,
401 | },
402 | }
403 | );
404 | if (result_chat_update.modifiedCount === 0) {
405 | reject({ text: "No matching documents found" });
406 | return;
407 | }
408 | resolve(result);
409 | } catch (err) {
410 | reject(err); // Reject with the caught error
411 | }
412 | });
413 | },
414 | };
415 |
--------------------------------------------------------------------------------
/client/src/components/menu/style.scss:
--------------------------------------------------------------------------------
1 | header {
2 | display: none;
3 | }
4 |
5 | .exitMenu {
6 | display: none;
7 | }
8 | .details-modal {
9 | background: #ffffff;
10 | border-radius: 0.5em;
11 | box-shadow: 0 10px 20px rgba(black, 0.2);
12 | left: 50%;
13 | max-width: 90%;
14 | pointer-events: none;
15 | position: absolute;
16 | top: 50%;
17 | transform: translate(-50%, -50%);
18 | width: 30em;
19 | text-align: left;
20 | max-height: 90vh;
21 | display: flex;
22 | flex-direction: column;
23 |
24 | // -------------- CLOSE
25 |
26 | .details-modal-close {
27 | align-items: center;
28 | color: #111827;
29 | display: flex;
30 | height: 4.5em;
31 | justify-content: center;
32 | pointer-events: none;
33 | position: absolute;
34 | right: 0;
35 | top: 0;
36 | width: 4.5em;
37 |
38 | svg {
39 | display: block;
40 | }
41 | }
42 |
43 | // -------------- TITLE
44 |
45 | .details-modal-title {
46 | color: #111827;
47 | padding: 1.5em 2em;
48 | pointer-events: all;
49 | position: relative;
50 | width: calc(100% - 4.5em);
51 |
52 | h1 {
53 | font-size: 1.25rem;
54 | font-weight: 600;
55 | line-height: normal;
56 | }
57 | }
58 |
59 | // -------------- CONTENT
60 |
61 | .details-modal-content {
62 | border-top: 1px solid #e0e0e0;
63 | padding: 2em;
64 | pointer-events: all;
65 | overflow: auto;
66 | }
67 | }
68 |
69 | // -------------- OVERLAY
70 |
71 | .details-modal-overlay {
72 | transition: opacity 0.2s ease-out;
73 | pointer-events: none;
74 | background: rgba(#0f172a, 0.8);
75 | position: fixed;
76 | opacity: 0;
77 | bottom: 0;
78 | right: 0;
79 | left: 0;
80 | top: 0;
81 |
82 | details[open] & {
83 | pointer-events: all;
84 | opacity: 0.5;
85 | }
86 | }
87 |
88 | @media screen and (max-width: 767px) {
89 | .exitMenu {
90 | display: none;
91 | position: fixed;
92 | width: calc(100vw - 260px);
93 | height: 100%;
94 | background: rgba(86, 88, 105, 0.75);
95 | top: 0;
96 | z-index: 2000;
97 | left: 260px;
98 |
99 | button {
100 | width: 40px;
101 | height: 40px;
102 | border: none;
103 | background: transparent;
104 | margin-top: 10px;
105 | margin-left: 10px;
106 | cursor: pointer;
107 | outline: none;
108 |
109 | &:focus {
110 | border: 2px solid white;
111 | }
112 |
113 | svg {
114 | color: white;
115 | width: 24px;
116 | height: 24px;
117 | }
118 | }
119 | }
120 |
121 | header {
122 | display: grid;
123 | grid-template-columns: auto auto auto;
124 | text-align: center;
125 | position: fixed;
126 | top: 0;
127 | width: 100%;
128 | background: rgba(52, 53, 65, 1);
129 | padding-top: 0.5em;
130 | padding-bottom: 0.3em;
131 | color: rgba(217, 217, 227, 1);
132 | z-index: 1000;
133 | box-shadow: 0px 0px 1px 1px hsla(0, 0%, 100%, 0.2);
134 |
135 | button {
136 | background: transparent;
137 | padding: 0;
138 | margin: 0;
139 | border: 0;
140 | color: rgba(217, 217, 227, 1);
141 | cursor: pointer;
142 | }
143 |
144 | .start {
145 | text-align: left;
146 | padding-left: 0.5em;
147 | }
148 |
149 | svg {
150 | width: 24px;
151 | height: 24px;
152 | }
153 |
154 | .title {
155 | margin-left: 1rem;
156 | margin-right: 0.8rem;
157 | padding-top: 3px;
158 | white-space: nowrap;
159 | text-overflow: ellipsis;
160 | overflow: hidden;
161 | }
162 |
163 | .end {
164 | text-align: right;
165 | padding-right: 0.5em;
166 | }
167 | }
168 | }
169 |
170 | .menu {
171 | position: fixed;
172 | z-index: 2000;
173 | width: 260px;
174 | background: #191949; // Changes
175 | height: 100%;
176 | display: flex;
177 | flex-direction: column;
178 |
179 | @media screen and (max-width: 767px) {
180 | display: none;
181 |
182 | &:is(.showMd),
183 | &:is(.showMd) + .exitMenu {
184 | display: flex;
185 | }
186 | }
187 |
188 | button {
189 | position: relative;
190 | color: rgba(255, 255, 255, 1);
191 | width: 100%;
192 | transition: all 0.2s ease-in-out;
193 | border: none;
194 | padding-top: 0.75rem;
195 | padding-bottom: 0.75rem;
196 | padding-left: 3em;
197 | border-radius: 0.375rem;
198 | background: transparent;
199 | text-align: left;
200 | font-weight: normal;
201 | font-size: 0.875rem;
202 | line-height: 1.25rem;
203 |
204 | &:hover {
205 | background: hsla(240, 9%, 59%, 0.1);
206 | cursor: pointer;
207 | }
208 |
209 | svg {
210 | position: absolute;
211 | padding-top: 2px;
212 | left: 0.75rem;
213 | width: 16px;
214 | height: 16px;
215 | }
216 | }
217 |
218 | button[aria-label="new"] {
219 | border: 1px solid hsla(0, 0%, 100%, 0.2);
220 | width: calc(260px - 1rem);
221 | margin-top: 0.5rem;
222 | margin-bottom: 0.6rem;
223 | margin-left: 0.5rem;
224 | }
225 |
226 | .history {
227 | height: 100%;
228 | overflow-y: auto;
229 | overflow-x: hidden;
230 | margin-bottom: 0.5rem;
231 | width: 259px;
232 |
233 | &::-webkit-scrollbar {
234 | width: 8px;
235 | }
236 |
237 | &::-webkit-scrollbar-track {
238 | background: transparent;
239 | }
240 |
241 | &::-webkit-scrollbar-thumb {
242 | background: rgba(86, 88, 105, 1);
243 | border-radius: 30px;
244 |
245 | &:hover {
246 | background: rgba(172, 172, 190, 1);
247 | }
248 | }
249 |
250 | .delete-btn {
251 | background: none;
252 | }
253 |
254 | button {
255 | width: calc(260px - 1rem);
256 | margin-left: 0.5rem;
257 | margin-bottom: 5px;
258 | padding-right: 0.75rem;
259 | white-space: nowrap;
260 | text-overflow: ellipsis;
261 | overflow: hidden;
262 | }
263 |
264 | .active {
265 | background: rgba(52, 53, 65, 1);
266 | }
267 | }
268 |
269 | .actions {
270 | width: calc(260px - 1rem);
271 | bottom: 0;
272 | border-top: 1px solid hsla(0, 0%, 100%, 0.2);
273 | padding-top: 5px;
274 | padding-bottom: 5px;
275 | background: #191949; //Changes
276 | margin-left: 0.5rem;
277 |
278 | button {
279 | margin-bottom: 3px;
280 |
281 | &:hover {
282 | background: hsla(240, 9%, 59%, 0.1);
283 | }
284 |
285 | svg {
286 | position: absolute;
287 | padding-top: 2px;
288 | left: 0.75rem;
289 | width: 16px;
290 | height: 16px;
291 | }
292 |
293 | img {
294 | position: absolute;
295 | left: 0.75rem;
296 | }
297 |
298 | span {
299 | margin-top: -2px;
300 | position: absolute;
301 | color: rgba(52, 53, 65, 1);
302 | background: rgba(250, 230, 158, 1);
303 | border-radius: 0.375rem;
304 | padding-left: 0.375rem;
305 | padding-right: 0.375rem;
306 | padding-top: 0.125rem;
307 | padding-bottom: 0.125rem;
308 | right: 0.75rem;
309 | }
310 | }
311 | }
312 | }
313 |
314 | .settingsModal {
315 | display: none;
316 | align-items: center;
317 | justify-content: center;
318 | position: fixed;
319 | z-index: 2100;
320 | width: 100%;
321 | height: 100%;
322 | overflow: hidden;
323 | transition: all 0.15s ease-in-out;
324 | -moz-transition: all 0.15s ease-in-out;
325 |
326 | @media screen and (max-width: 640px) {
327 | align-items: end;
328 | }
329 |
330 | .inner {
331 | display: block;
332 | background: white;
333 | padding: 1.5rem;
334 | border-radius: 0.5rem;
335 | height: auto;
336 | width: auto;
337 | margin: 1rem;
338 |
339 | .content {
340 | display: flex;
341 | flex-direction: column;
342 | gap: 20px;
343 |
344 | .content-input {
345 | display: flex;
346 | flex-direction: column;
347 | gap: 4px;
348 | }
349 | .content-submit {
350 | display: flex;
351 | justify-content: space-between;
352 | div {
353 | display: flex;
354 | gap: 4px;
355 | }
356 |
357 | .content-button {
358 | text-align: center;
359 | height: 30px;
360 | width: 100px;
361 | color: #ffffff;
362 | background: #191949;
363 | transition: all 0.1s ease-in-out;
364 | -moz-transition: all 0.1s ease-in-out;
365 | border-radius: 3px;
366 | border: none;
367 | outline: none;
368 | font-size: 16px;
369 | }
370 | }
371 | input {
372 | height: 30px;
373 | width: 95%;
374 | color: #2d333a;
375 | border: 1px solid #c2c8d0;
376 | background: transparent;
377 | border-radius: 3px;
378 | line-height: 1.1;
379 | outline: none;
380 | font-size: 16px;
381 | padding: 0.5rem 0.5rem;
382 | }
383 |
384 | &:is(.top) {
385 | h3 {
386 | font-weight: normal;
387 | line-height: 1.5rem;
388 | }
389 |
390 | button {
391 | background: none;
392 | border: none;
393 | width: 100%;
394 | text-align: right;
395 | cursor: pointer;
396 |
397 | svg {
398 | height: 20px;
399 | width: 20px;
400 | }
401 | }
402 | }
403 |
404 | &:is(.ceneter) {
405 | margin-top: 1rem;
406 | margin-bottom: 1rem;
407 |
408 | p {
409 | font-size: 0.875rem;
410 | line-height: 1.25rem;
411 | }
412 |
413 | button {
414 | width: 2.75rem;
415 | height: 1.5rem;
416 | border: none;
417 | border-radius: 9999px;
418 | position: relative;
419 | cursor: pointer;
420 |
421 | div {
422 | position: absolute;
423 | width: calc(1.5rem - 4px);
424 | border-radius: 50%;
425 | background: #fff;
426 | color: #fff;
427 | top: 2px;
428 | bottom: 2px;
429 | transition: all 0.15s ease-in-out;
430 | -moz-transition: all 0.15s ease-in-out;
431 | }
432 | }
433 | }
434 | }
435 |
436 | .bottum {
437 | button {
438 | font-size: 0.875rem;
439 | line-height: 1.25rem;
440 | background: none;
441 | border: none;
442 | margin-right: 1rem;
443 | cursor: pointer;
444 |
445 | // &:is(.end) {
446 | // padding-left: 1rem;
447 | // }
448 | }
449 | }
450 | }
451 | }
452 |
453 | .light {
454 | .settingsModal {
455 | background: hsla(240, 9%, 59%, 0.9);
456 |
457 | .inner {
458 | background: #fff;
459 |
460 | .top {
461 | h3 {
462 | color: rgba(32, 33, 35, 1);
463 | }
464 |
465 | svg {
466 | color: rgba(32, 33, 35, 1);
467 | }
468 | }
469 |
470 | .ceneter {
471 | p {
472 | color: rgba(86, 88, 105, 1);
473 | }
474 |
475 | button {
476 | background: rgba(217, 217, 227, 1);
477 |
478 | div {
479 | transform: translateX(2px);
480 | }
481 | }
482 | }
483 | }
484 |
485 | .bottum {
486 | button {
487 | color: rgba(86, 88, 105, 1);
488 | text-decoration: underline;
489 |
490 | // &:is(.end) {
491 | // border-left: 1px solid rgba(86, 88, 105, 1);
492 | // }
493 | }
494 | }
495 | }
496 | }
497 |
498 | .dark {
499 | .settingsModal {
500 | background: rgba(52, 53, 65, 0.9);
501 |
502 | .inner {
503 | background: rgba(32, 33, 35, 1);
504 |
505 | .top {
506 | h3 {
507 | color: #fff;
508 | }
509 |
510 | svg {
511 | color: #fff;
512 | }
513 | }
514 |
515 | .ceneter {
516 | p {
517 | color: rgba(197, 197, 210, 1);
518 | }
519 |
520 | button {
521 | background: #191949; //Changes
522 |
523 | div {
524 | transform: translateX(calc(2.75rem - 1.5rem + 2px));
525 | }
526 | }
527 | }
528 | }
529 |
530 | .bottum {
531 | button {
532 | color: rgba(197, 197, 210, 1);
533 | text-decoration: underline;
534 |
535 | // &:is(.end) {
536 | // border-left: 1px solid rgba(197, 197, 210, 1);
537 | // }
538 | }
539 | }
540 | }
541 | }
542 |
--------------------------------------------------------------------------------
/client/src/page/chat.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useReducer, useRef, useState } from "react";
2 | import { Reload, Rocket, Stop } from "../assets";
3 | import { Chat, New } from "../components";
4 | import { useLocation, useNavigate, useParams } from "react-router-dom";
5 | import { setLoading } from "../redux/loading";
6 | import { useDispatch, useSelector } from "react-redux";
7 | import { addList, emptyAllRes, insertNew, livePrompt } from "../redux/messages";
8 | import { emptyUser } from "../redux/user";
9 | import { useContext } from "react";
10 | import { documentsContext } from "./../App";
11 | import instance from "../config/instance";
12 | import Upload from "./../assets/upload";
13 | import "./style.scss";
14 |
15 | const reducer = (state, { type, status }) => {
16 | switch (type) {
17 | case "chat":
18 | return {
19 | chat: status,
20 | loading: status,
21 | resume: status,
22 | actionBtns: false,
23 | };
24 | case "error":
25 | return {
26 | chat: true,
27 | error: status,
28 | resume: state.resume,
29 | loading: state.loading,
30 | actionBtns: state.actionBtns,
31 | };
32 | case "resume":
33 | return {
34 | chat: true,
35 | resume: status,
36 | loading: status,
37 | actionBtns: true,
38 | };
39 | default:
40 | return state;
41 | }
42 | };
43 |
44 | const Main = ({ file_id, set_file_id }) => {
45 | let location = useLocation();
46 | const navigate = useNavigate();
47 | const dispatch = useDispatch();
48 | const chatRef = useRef();
49 | const { user } = useSelector((state) => state);
50 | const { id = null } = useParams();
51 | const { _id } = useSelector((state) => state.messages);
52 | const { documents, getFiles } = useContext(documentsContext);
53 | const [status, stateAction] = useReducer(reducer, {
54 | chat: false,
55 | error: false,
56 | actionBtns: false,
57 | });
58 |
59 | useEffect(() => {
60 | getFiles();
61 | }, [_id]);
62 |
63 | useEffect(() => {
64 | if (user) {
65 | dispatch(emptyAllRes());
66 | setTimeout(() => {
67 | if (id) {
68 | const getSaved = async () => {
69 | let res = null;
70 | try {
71 | res = await instance.get("/api/chat/saved", {
72 | params: {
73 | chatId: id,
74 | },
75 | });
76 | } catch (err) {
77 | console.log(err);
78 | if (err?.response?.data?.status === 404) {
79 | navigate("/404");
80 | } else {
81 | alert(err);
82 | dispatch(setLoading(false));
83 | }
84 | } finally {
85 | if (res?.data) {
86 | dispatch(addList({ _id: id, items: res?.data?.data }));
87 | stateAction({ type: "resume", status: false });
88 | dispatch(setLoading(false));
89 | }
90 | }
91 | };
92 |
93 | getSaved();
94 | } else {
95 | stateAction({ type: "chat", status: false });
96 | dispatch(setLoading(false));
97 | }
98 | }, 1000);
99 | }
100 | }, [location]);
101 |
102 | return (
103 |
104 |
105 | {status.chat ? : }
106 |
107 |
108 |
117 |
118 | );
119 | };
120 |
121 | export default Main;
122 |
123 | //Input Area
124 | const InputArea = ({
125 | status,
126 | chatRef,
127 | stateAction,
128 | file_id,
129 | set_file_id,
130 | getFiles,
131 | documents,
132 | }) => {
133 | let textAreaRef = useRef();
134 | const navigate = useNavigate();
135 | const dispatch = useDispatch();
136 | const [last_prompt, set_last_prompt] = useState(null);
137 | let { prompt, content, _id } = useSelector((state) => state.messages);
138 | console.log(_id);
139 |
140 | useEffect(() => {
141 | getFiles();
142 | }, [_id]);
143 | useEffect(() => {
144 | textAreaRef.current?.addEventListener("input", (e) => {
145 | textAreaRef.current.style.height = "auto";
146 | textAreaRef.current.style.height =
147 | textAreaRef.current.scrollHeight + "px";
148 | });
149 | });
150 | const handleFileUpload = async (e) => {
151 | let response = null;
152 | try {
153 | const file = e.target.files[0];
154 | console.log(file);
155 | if (
156 | file.name.endsWith(".pdf") ||
157 | file.name.endsWith(".txt") ||
158 | file.name.endsWith(".csv") ||
159 | file.name.endsWith(".docx")
160 | ) {
161 | const formData = new FormData();
162 | formData.append("file", file);
163 | formData.append("chatId", _id);
164 | response = await instance.post("/api/chat/upload", formData, {
165 | headers: {
166 | "Content-Type": "multipart/form-data",
167 | },
168 | });
169 | console.log(response.data);
170 | } else {
171 | alert("File type not supported");
172 | }
173 | } catch (error) {
174 | console.log(error);
175 | } finally {
176 | if (response?.data?.data?.chatId) {
177 | dispatch(insertNew({ _id: response?.data?.data?.chatId }));
178 | console.log(response?.data?.data?.chatId);
179 | navigate(`/chat/${response?.data?.data?.chatId}`);
180 | alert("File uploaded successfully");
181 | getFiles();
182 | } else {
183 | alert(`File upload failed due to unsupported file type`);
184 | }
185 | }
186 | };
187 |
188 | const deleteFile = async (e) => {
189 | let response = null;
190 | console.log(_id);
191 | try {
192 | const file = e.target.textContent;
193 | console.log(file, _id);
194 | response = await instance.post("/api/chat/deletefile", {
195 | chatId: _id,
196 | file_name: file,
197 | });
198 | console.log(response?.data);
199 | } catch (error) {
200 | console.log(error);
201 | } finally {
202 | if (response?.status === 200) {
203 | getFiles();
204 | } else {
205 | alert("File delete failed");
206 | }
207 | }
208 | };
209 |
210 | const FormHandle = async () => {
211 | prompt = last_prompt;
212 | if (prompt?.length > 0) {
213 | chatRef?.current?.clearResponse();
214 | stateAction({ type: "chat", status: true });
215 |
216 | let chatsId = Date.now();
217 |
218 | dispatch(insertNew({ id: chatsId, content: "", prompt }));
219 |
220 | let res = null;
221 |
222 | try {
223 | if (_id) {
224 | console.log(prompt, content, _id);
225 | dispatch(livePrompt(""));
226 | res = await instance.put("/api/chat", {
227 | chatId: _id,
228 | prompt,
229 | file_id,
230 | });
231 | console.log("PUT", res.data);
232 | } else {
233 | dispatch(livePrompt(""));
234 | res = await instance.post("/api/chat", {
235 | prompt,
236 | file_id,
237 | chatId: _id,
238 | });
239 | console.log("POST", res.data);
240 | navigate(`/chat/${res?.data?.data?._id}`);
241 | }
242 | } catch (err) {
243 | console.log(err.response.data);
244 | if (err?.response?.data?.status === 405) {
245 | dispatch(emptyUser());
246 | dispatch(emptyAllRes());
247 | navigate("/login");
248 | } else {
249 | stateAction({ type: "error", status: true });
250 | }
251 | } finally {
252 | if (res?.data) {
253 | set_file_id(null);
254 | const { _id, content } = res?.data?.data;
255 | console.log(_id, content);
256 | dispatch(insertNew({ _id, fullContent: content, chatsId }));
257 |
258 | chatRef?.current?.loadResponse(stateAction, content, chatsId);
259 |
260 | stateAction({ type: "error", status: false });
261 | }
262 | }
263 | }
264 | };
265 |
266 | useEffect(() => {
267 | const handleInput = (e) => {
268 | if (e.key === "Enter" && e.shiftKey) {
269 | e.preventDefault();
270 | FormHandle(textAreaRef.current.value.trim());
271 | textAreaRef.current.value = "";
272 | }
273 | };
274 |
275 | textAreaRef.current?.addEventListener("keydown", handleInput);
276 |
277 | return () => {
278 | textAreaRef.current?.removeEventListener("keydown", handleInput);
279 | };
280 | }, [FormHandle]);
281 |
282 | return (
283 |
284 | {!status.error ? (
285 | <>
286 |
287 | {status.chat && content?.length > 0 && status.actionBtns && (
288 | <>
289 | {!status?.resume ? (
290 |
298 | ) : (
299 |
306 | )}
307 | >
308 | )}
309 |
310 |
311 |
312 |
313 |
323 |
324 |
325 | {/*
326 | {documents?.length > 0 &&
327 | documents?.map((doc, index) => {
328 | return (
329 |
330 |
331 | {doc}
332 |
333 |
334 | );
335 | })}
336 |
*/}
337 |
363 |
364 | {status.chat && content?.length > 0 && status.actionBtns && (
365 | <>
366 | {!status?.resume ? (
367 |
368 |
375 |
376 | ) : (
377 |
378 |
385 |
386 | )}
387 | >
388 | )}
389 |
390 |
391 | >
392 | ) : (
393 |
394 |
There was an error generating a response
395 |
399 |
400 | )}
401 |
402 | );
403 | };
404 |
--------------------------------------------------------------------------------
/client/src/page/style.scss:
--------------------------------------------------------------------------------
1 | @import "../index.scss";
2 |
3 | @keyframes sendingDot2 {
4 | 0% {
5 | background: transparent;
6 | }
7 |
8 | 50% {
9 | background: #8e8e40;
10 | }
11 |
12 | 100% {
13 | background: #8e8e40;
14 | }
15 | }
16 |
17 | @keyframes sendingDot3 {
18 | 0% {
19 | background: transparent;
20 | }
21 |
22 | 50% {
23 | background: transparent;
24 | }
25 |
26 | 100% {
27 | background: #8e8e40;
28 | }
29 | }
30 |
31 | .files-div {
32 | display: flex;
33 | flex-direction: column;
34 |
35 | .files {
36 | padding: 0 10px;
37 | display: flex;
38 | justify-content: left;
39 | gap: 10px;
40 | flex-wrap: wrap;
41 | max-width: 900px;
42 |
43 | div {
44 | display: flex;
45 | flex-wrap: wrap;
46 | }
47 | .file-name {
48 | padding: 10px 5px;
49 | border-radius: 5px;
50 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
51 | }
52 | }
53 | }
54 |
55 | .light {
56 | .Error {
57 | background: white;
58 |
59 | .code {
60 | color: $ColorBlack;
61 | border-right: 1px solid $ColorBlack;
62 | }
63 |
64 | .txt {
65 | color: $ColorBlack;
66 | }
67 | }
68 |
69 | // input area
70 | .inputArea {
71 | background-image: linear-gradient(
72 | 180deg,
73 | hsla(0, 0%, 100%, 0) 13.94%,
74 | #fff 54.73%
75 | );
76 |
77 | @media screen and (max-width: 767px) {
78 | border-top: 1px solid #d9d9e3;
79 | background-image: none;
80 | background: rgba(255, 255, 255, 1);
81 | }
82 |
83 | .chatActionsLg button {
84 | background: rgba(255, 255, 255, 1);
85 | color: rgba(64, 65, 79, 1);
86 | border: 1px solid rgba(0, 0, 0, 0.1);
87 |
88 | svg {
89 | color: rgba(64, 65, 79, 1);
90 | }
91 |
92 | &:hover {
93 | background: rgba(236, 236, 241, 1);
94 | }
95 | }
96 |
97 | .chatActionsMd button {
98 | color: rgba(64, 65, 79, 1) !important;
99 |
100 | &:hover {
101 | background: rgba(236, 236, 241, 1) !important;
102 | }
103 | }
104 |
105 | .box {
106 | border: 1px solid rgba(0, 0, 0, 0.1);
107 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
108 | background: #ffffff;
109 |
110 | textarea {
111 | color: #333;
112 |
113 | &::-webkit-scrollbar-thumb {
114 | background: rgba(236, 236, 241, 1);
115 | border-radius: 30px;
116 | }
117 | }
118 |
119 | button:hover {
120 | background: #d9d9e3 !important;
121 | }
122 | }
123 |
124 | .text {
125 | background: rgba(255, 255, 255, 1);
126 | color: rgba(0, 0, 0, 0.5);
127 | text-align: center;
128 |
129 | a {
130 | color: rgba(0, 0, 0, 0.5);
131 | text-decoration-color: rgba(0, 0, 0, 0.5);
132 | }
133 | }
134 |
135 | .error p {
136 | color: #000000;
137 | }
138 | }
139 | }
140 |
141 | .dark {
142 | .Error {
143 | background: rgba(52, 53, 65, 1);
144 |
145 | .code {
146 | border-right: 1px solid #d9d9e3;
147 | color: #d9d9e3;
148 | }
149 |
150 | .txt {
151 | color: #d9d9e3;
152 | }
153 | }
154 |
155 | // input area
156 | .inputArea {
157 | background-image: linear-gradient(
158 | 180deg,
159 | rgba(53, 55, 64, 0),
160 | #353740 58.85%
161 | );
162 |
163 | @media screen and (max-width: 767px) {
164 | border-top: 1px solid hsla(0, 0%, 100%, 0.2);
165 | background-image: none;
166 | background: rgba(52, 53, 65, 1);
167 | }
168 |
169 | .chatActionsLg button {
170 | color: #d9d9e3;
171 | background-color: rgba(52, 53, 65, 1);
172 | border: 1px solid rgba(86, 88, 105, 1);
173 |
174 | svg {
175 | color: #d9d9e3;
176 | }
177 |
178 | &:hover {
179 | background: rgba(64, 65, 79, 1);
180 | }
181 | }
182 |
183 | .chatActionsMd button {
184 | color: white !important;
185 |
186 | &:hover {
187 | background: #40414f !important;
188 | }
189 | }
190 |
191 | .box {
192 | border: 1px solid rgba(32, 33, 35, 0.5);
193 | box-shadow: 0 0 15px rgba(0, 0, 0, 0.1);
194 | background: #40414f;
195 |
196 | textarea {
197 | color: black;
198 |
199 | &::-webkit-scrollbar-thumb {
200 | background: rgba(86, 88, 105, 1);
201 | border-radius: 30px;
202 | }
203 | }
204 |
205 | button:hover {
206 | background: rgba(32, 33, 35, 1) !important;
207 | }
208 | }
209 |
210 | .text {
211 | // background: rgba(52, 53, 65, 1);
212 | color: hsla(0, 0%, 100%, 0.5);
213 |
214 | a {
215 | color: hsla(0, 0%, 100%, 0.5);
216 | text-decoration-color: hsla(0, 0%, 100%, 0.5);
217 | }
218 | }
219 |
220 | .error p {
221 | color: #ffffff;
222 | }
223 | }
224 | }
225 |
226 | .Error {
227 | position: fixed;
228 | z-index: 4000;
229 | top: 0;
230 | bottom: 0;
231 | left: 0;
232 | right: 0;
233 | height: 100%;
234 | width: 100%;
235 |
236 | .flex {
237 | height: 100%;
238 | display: flex;
239 | flex-direction: row;
240 | align-items: center;
241 | justify-content: center;
242 |
243 | .code {
244 | display: flex;
245 | justify-content: center;
246 | align-items: center;
247 | padding-right: 1.5rem;
248 | height: 3rem;
249 | margin-right: 1.5rem;
250 | font-size: 24px;
251 | font-weight: 500;
252 | }
253 |
254 | .txt {
255 | font-size: 14px;
256 | font-weight: normal;
257 | }
258 | }
259 | }
260 |
261 | .main {
262 | width: 100%;
263 | display: block;
264 | position: relative;
265 |
266 | .contentArea {
267 | height: auto;
268 | align-items: center;
269 | text-align: center;
270 | }
271 |
272 | .inputArea {
273 | position: fixed;
274 | z-index: 1000;
275 | bottom: 0;
276 | height: auto;
277 | min-height: 17%;
278 | width: calc(100vw - 260px) !important;
279 |
280 | @media screen and (max-width: 767px) {
281 | width: 100% !important;
282 | }
283 |
284 | .chatActionsLg {
285 | padding-top: 0.5rem;
286 | display: flex;
287 | justify-content: center;
288 | align-items: center;
289 |
290 | button {
291 | position: relative;
292 | font-size: 0.875rem;
293 | line-height: 1.25rem;
294 | padding: 0.5rem 0.75rem;
295 | padding-left: 1.85rem;
296 | border-radius: 3px;
297 | cursor: pointer;
298 |
299 | svg {
300 | left: 0.75rem;
301 | padding-top: 0.25rem;
302 | position: absolute;
303 | width: 0.75rem;
304 | height: 0.75rem;
305 | }
306 | }
307 |
308 | @media screen and (max-width: 767px) {
309 | display: none;
310 | }
311 | }
312 |
313 | .flexBody {
314 | display: flex;
315 | flex-direction: row;
316 | padding-left: 10em;
317 | padding-right: 10em;
318 | padding-top: 0.5em;
319 | background: transparent;
320 | gap: 1em;
321 |
322 | @media only screen and (max-width: 1300px) {
323 | padding-left: 3em;
324 | padding-right: 3em;
325 | }
326 |
327 | @media only screen and (max-width: 1000px) {
328 | padding-left: 1em;
329 | padding-right: 1em;
330 | }
331 |
332 | @media only screen and (max-width: 767px) {
333 | padding-left: 0.5em;
334 | padding-right: 0.5em;
335 | }
336 |
337 | .upload-file {
338 | border-radius: 0.5rem;
339 | display: flex;
340 | justify-content: center;
341 | align-items: center;
342 | padding: 0 0.5rem;
343 | border: 2px solid #191949;
344 | height: 40px;
345 | margin: auto 0 0 0;
346 | }
347 |
348 | .chatActionsMd {
349 | display: none;
350 |
351 | @media only screen and (max-width: 767px) {
352 | display: block;
353 | padding-left: 0.5em;
354 |
355 | button {
356 | padding: 0 1rem;
357 | height: 100%;
358 | border-radius: 0.25rem;
359 | font-size: 0.875rem;
360 | cursor: pointer;
361 | border: none;
362 | background: transparent;
363 | }
364 | }
365 | }
366 |
367 | .box {
368 | width: 100%;
369 | display: flex;
370 | flex-direction: column;
371 | position: relative;
372 | border-radius: 0.375rem;
373 | padding-top: 1rem;
374 | border: 2px solid #191949;
375 | background: #e5f3fd;
376 | gap: 10px;
377 |
378 | textarea {
379 | outline: none;
380 | border-bottom-left-radius: 0.375rem;
381 | border-top-left-radius: 0.375rem;
382 | width: 90%;
383 | height: 30px;
384 | outline: none;
385 | border: none;
386 | padding-left: 1rem;
387 | padding-right: calc(9% + 2px);
388 | background: transparent;
389 | resize: none;
390 | font-weight: normal;
391 | overflow-y: auto;
392 | max-height: 12em;
393 |
394 | &::-webkit-scrollbar {
395 | width: 8px;
396 | }
397 |
398 | &::-webkit-scrollbar-track {
399 | background: transparent;
400 | }
401 | }
402 |
403 | textArea::placeholder {
404 | font-size: 16px;
405 | }
406 |
407 | button {
408 | position: absolute;
409 | bottom: 0;
410 | right: 0;
411 | border: none;
412 | outline: none;
413 | background: transparent;
414 | padding: 0.25rem;
415 | border-radius: 0.375rem;
416 | margin-right: 10px;
417 | margin-bottom: 5px;
418 | cursor: pointer;
419 |
420 | svg {
421 | width: 16px;
422 | height: 16px;
423 | color: rgba(142, 142, 160, 1);
424 | }
425 | }
426 |
427 | .loading {
428 | display: flex;
429 | gap: 2px;
430 | position: absolute;
431 | bottom: 0;
432 | right: 0;
433 | background: transparent;
434 | padding: 0.25rem;
435 | margin-right: 10px;
436 | margin-bottom: 1rem;
437 |
438 | .dot {
439 | width: 3px;
440 | height: 3px;
441 | background: #8e8e40;
442 | border-radius: 50%;
443 | border: none;
444 | }
445 |
446 | .dot-2 {
447 | animation: sendingDot2 1s ease-in-out infinite;
448 | }
449 |
450 | .dot-3 {
451 | animation: sendingDot3 1s ease-in-out infinite;
452 | }
453 | }
454 | }
455 | }
456 |
457 | .error {
458 | display: flex;
459 | flex-direction: column;
460 | justify-content: center;
461 | align-items: center;
462 |
463 | p {
464 | font-size: 0.75rem;
465 | line-height: 1rem;
466 | margin-top: 0.5rem;
467 | margin-bottom: 0.5rem;
468 | }
469 |
470 | button {
471 | position: relative;
472 | font-size: 0.875rem;
473 | line-height: 1.25rem;
474 | padding: 0.5rem 0.75rem;
475 | padding-left: 1.85rem;
476 | border-radius: 0.25rem;
477 | cursor: pointer;
478 | background: rgba(16, 163, 127, 1);
479 | color: rgba(255, 255, 255, 1);
480 | border: none;
481 |
482 | svg {
483 | left: 0.75rem;
484 | padding-top: 0.25rem;
485 | position: absolute;
486 | width: 0.75rem;
487 | height: 0.75rem;
488 | color: rgba(255, 255, 255, 1);
489 | }
490 |
491 | &:hover {
492 | background: #191949;
493 | }
494 | }
495 | }
496 |
497 | .text {
498 | padding-top: 0.75rem;
499 | padding-bottom: 1.5rem;
500 | padding-left: 7rem;
501 | padding-right: 7rem;
502 | font-size: 0.75rem;
503 | line-height: 1rem;
504 |
505 | a {
506 | font-size: 0.75rem;
507 | }
508 |
509 | @media screen and (max-width: 1300px) {
510 | padding-left: 1rem;
511 | padding-right: 1rem;
512 | }
513 | }
514 | }
515 | }
516 |
517 | .Auth {
518 | display: block;
519 | position: fixed;
520 | z-index: 1000;
521 | height: 100%;
522 | width: 100%;
523 | background: white;
524 | color: #000000;
525 |
526 | .inner {
527 | display: flex;
528 | height: 100%;
529 | width: 100%;
530 | flex-direction: column;
531 | overflow-y: auto;
532 |
533 | .suggection {
534 | height: 100%;
535 | display: flex;
536 | flex-direction: column;
537 | align-items: center;
538 | justify-content: center;
539 |
540 | svg {
541 | margin-bottom: 0.5rem;
542 | }
543 |
544 | p {
545 | text-align: center;
546 | margin-bottom: 0.5rem;
547 | }
548 |
549 | .btns {
550 | display: flex;
551 | flex-direction: row;
552 | gap: 10px;
553 | margin-top: 0.5rem;
554 |
555 | button {
556 | width: 5em;
557 | height: 3em;
558 | text-align: center;
559 | background: #191949;
560 | color: rgba(255, 255, 255, 1);
561 | border-radius: 0.25rem;
562 | border: none;
563 | cursor: pointer;
564 | transition: all 0.1s ease-in-out;
565 | -moz-transition: all 0.1s ease-in-out;
566 |
567 | &:hover {
568 | background: #191949;
569 | }
570 | }
571 | }
572 | }
573 |
574 | .bottum {
575 | margin-top: auto;
576 | display: flex;
577 | flex-direction: row;
578 | width: 100%;
579 | padding: 1rem 0;
580 | align-items: center;
581 | justify-content: center;
582 |
583 | a {
584 | text-decoration: none;
585 | color: rgba($color: #000000, $alpha: 0.5);
586 | }
587 |
588 | .start {
589 | padding-right: 0.5rem;
590 | font-size: 0.75rem;
591 | border-right: 1px solid rgba($color: #000000, $alpha: 0.7);
592 | }
593 |
594 | .end {
595 | padding-left: 0.5rem;
596 | font-size: 0.75rem;
597 | }
598 | }
599 | }
600 | }
601 |
--------------------------------------------------------------------------------
/server/routes/chat.js:
--------------------------------------------------------------------------------
1 | import { Router } from "express";
2 | import dotnet from "dotenv";
3 | import user from "../helpers/user.js";
4 | import jwt from "jsonwebtoken";
5 | import chat from "../helpers/chat.js";
6 | import OpenAI, { toFile } from "openai";
7 | import { db } from "../db/connection.js";
8 | import collections from "../db/collections.js";
9 | import multer from "multer";
10 | import fs from "fs";
11 | import { ObjectId } from "mongodb";
12 | dotnet.config();
13 |
14 | let router = Router();
15 | const upload = multer({ dest: "uploads/" });
16 |
17 | const CheckUser = async (req, res, next) => {
18 | jwt.verify(
19 | req.cookies?.userToken,
20 | process.env.JWT_PRIVATE_KEY,
21 | async (err, decoded) => {
22 | if (decoded) {
23 | let userData = null;
24 |
25 | try {
26 | userData = await user.checkUserFound(decoded);
27 | } catch (err) {
28 | if (err?.notExists) {
29 | res.clearCookie("userToken").status(405).json({
30 | status: 405,
31 | message: err?.text,
32 | });
33 | } else {
34 | res.status(500).json({
35 | status: 500,
36 | message: err,
37 | });
38 | }
39 | } finally {
40 | if (userData) {
41 | req.body.userId = userData._id;
42 | next();
43 | }
44 | }
45 | } else {
46 | res.status(405).json({
47 | status: 405,
48 | message: "Not Logged",
49 | });
50 | }
51 | }
52 | );
53 | };
54 |
55 | const client = new OpenAI({
56 | apiKey: "api-key",
57 | });
58 | const openai = new OpenAI({ apiKey: "api-key"});
59 |
60 | router.get("/", (req, res) => {
61 | res.send("Welcome to chatGPT api v1");
62 | });
63 |
64 | router.get("/upload", CheckUser, async (req, res) => {
65 | const { userId } = req.body;
66 | const { chatId } = req.query;
67 | let chat = await db.collection(collections.CHAT).findOne({
68 | user: userId.toString(),
69 | "data.chatId": chatId,
70 | });
71 | if (chat) {
72 | chat = chat.data.filter((obj) => {
73 | return obj.chatId === chatId;
74 | });
75 | chat = chat[0];
76 | res.status(200).json({
77 | status: 200,
78 | message: "Success",
79 | data: chat.file_name,
80 | });
81 | } else {
82 | res.status(404).json({
83 | status: 404,
84 | message: "Not found",
85 | });
86 | }
87 | });
88 |
89 | router.post("/upload", upload.single("file"), CheckUser, async (req, res) => {
90 | // take file object from frontend upload to openai and store id and file name to mongo db
91 | const { userId, chatId } = req.body;
92 | const file = fs.createReadStream(req ? req.file.path : null);
93 | let response = null;
94 | try {
95 | response = await client.files.create({
96 | purpose: "assistants",
97 | file: file,
98 | });
99 | } catch (err) {
100 | console.log(err);
101 | res.status(500).json({
102 | status: 500,
103 | message: err,
104 | });
105 | return; // Exit early in case of an error
106 | }
107 | // delete the file from the uploads folder after uploading to openai
108 | let file_id = null;
109 | let file_name = null;
110 |
111 | if (response) {
112 | file_id = response.id;
113 | file_name = req.file.originalname;
114 |
115 | let chatIdToSend = null; // Variable to store the chatId to send in the response
116 |
117 | const chat = await db
118 | .collection(collections.CHAT)
119 | .aggregate([
120 | {
121 | $match: {
122 | user: userId.toString(),
123 | },
124 | },
125 | {
126 | $unwind: "$data",
127 | },
128 | {
129 | $match: {
130 | "data.chatId": chatId,
131 | },
132 | },
133 | {
134 | $project: {
135 | files: "$data.files",
136 | },
137 | },
138 | ])
139 | .toArray();
140 | let all_files = [];
141 | if (chat[0]?.files?.length > 0) {
142 | all_files = [...chat[0].files, file_id];
143 | } else {
144 | all_files = [file_id];
145 | }
146 | const assistant = await client.beta.assistants.create({
147 | name: "GE CoPilot",
148 | instructions:
149 | "You are a helpful and that answers what is asked. Retrieve the relevant information from the files.",
150 | tools: [{ type: "retrieval" }, { type: "code_interpreter" }],
151 | model: "gpt-4-0125-preview",
152 | file_ids: all_files,
153 | });
154 | if (chat.length>0) {
155 | chatIdToSend = chatId; // Use existing chatId
156 | await db.collection(collections.CHAT).updateOne(
157 | {
158 | user: userId.toString(),
159 | "data.chatId": chatId,
160 | },
161 | {
162 | $addToSet: {
163 | "data.$.files": file_id,
164 | "data.$.file_name": file_name,
165 | },
166 | $set: {
167 | "data.$.assistant_id": assistant.id,
168 | },
169 | }
170 | );
171 | } else {
172 | const newChatId = new ObjectId().toHexString();
173 | chatIdToSend = newChatId; // Use newly generated chatId
174 | await db.collection(collections.CHAT).updateOne(
175 | {
176 | user: userId.toString(),
177 | },
178 | {
179 | $push: {
180 | data: {
181 | chatId: newChatId,
182 | files: [file_id],
183 | file_name: [file_name],
184 | chats: [],
185 | chat: [],
186 | assistant_id: assistant.id,
187 | },
188 | },
189 | },
190 | {
191 | new: true,
192 | upsert: true,
193 | }
194 | );
195 | }
196 |
197 | res.status(200).json({
198 | status: 200,
199 | message: "Success",
200 | data: {
201 | file_id,
202 | file_name,
203 | chatId: chatIdToSend, // Send the correct chatId in the response
204 | },
205 | });
206 | }
207 | });
208 |
209 | router.post("/", CheckUser, async (req, res) => {
210 | const { prompt, userId } = req.body;
211 | let response = {};
212 | try {
213 | console.log("POST is being called", req.body);
214 | // If no file_id is given
215 | response.openai = await openai.chat.completions.create({
216 | model: "gpt-4-0125-preview",
217 | messages: [
218 | {
219 | role: "system",
220 | content:
221 | "You are a helpful and that answers what is asked. Dont show the mathematical steps if not asked.",
222 | },
223 | {
224 | role: "user",
225 | content: prompt,
226 | },
227 | ],
228 | top_p: 0.5,
229 | });
230 | if (response.openai.choices[0].message) {
231 | response.openai = response.openai.choices[0].message.content;
232 | let index = 0;
233 | for (let c of response["openai"]) {
234 | if (index <= 1) {
235 | if (c == "\n") {
236 | response.openai = response.openai.slice(1, response.openai.length);
237 | }
238 | } else {
239 | break;
240 | }
241 | index++;
242 | }
243 | response.db = await chat.newResponse(prompt, response, userId);
244 | }
245 | } catch (err) {
246 | console.log(err);
247 | res.status(500).json({
248 | status: 500,
249 | message: err,
250 | });
251 | } finally {
252 | if (response?.db && response?.openai) {
253 | res.status(200).json({
254 | status: 200,
255 | message: "Success",
256 | data: {
257 | _id: response.db["chatId"],
258 | content: response.openai,
259 | },
260 | });
261 | }
262 | }
263 | });
264 |
265 | router.put("/", CheckUser, async (req, res) => {
266 | const { prompt, userId, chatId } = req.body;
267 | console.log("PUT is being called", req.body);
268 | let mes = {
269 | role: "system",
270 | content:
271 | "You are a helpful and that answers what is asked. Dont show the mathematical steps if not asked.",
272 | };
273 | let full = "";
274 | let message = await chat.Messages(userId, chatId);
275 | message = message[0].chats;
276 | mes = [mes, ...message];
277 | mes = [
278 | ...mes,
279 | {
280 | role: "user",
281 | content: prompt,
282 | },
283 | ];
284 | let response = {};
285 | let new_chat = await db.collection(collections.CHAT).findOne({
286 | user: userId.toString(),
287 | data: { $elemMatch: { chatId: chatId } },
288 | });
289 | new_chat = new_chat.data.filter((obj) => {
290 | return obj.chatId === chatId;
291 | });
292 | new_chat = new_chat[0];
293 | const assistant_id = new_chat.assistant_id;
294 | try {
295 | if (assistant_id) {
296 | console.log("Assistant running");
297 |
298 | const thread = await client.beta.threads.create({
299 | messages: [
300 | {
301 | role: "user",
302 | content: prompt,
303 | },
304 | ],
305 | });
306 | const run = await client.beta.threads.runs.create(thread.id, {
307 | assistant_id: assistant_id,
308 | });
309 | let final_run = "";
310 | while (final_run.status !== "completed") {
311 | final_run = await client.beta.threads.runs.retrieve(thread.id, run.id);
312 | }
313 | console.log(final_run.status);
314 | const messages = await client.beta.threads.messages.list(thread.id);
315 | response = { openai: messages.data[0].content[0].text.value };
316 | if (response.openai) {
317 | let index = 0;
318 | for (let c of response["openai"]) {
319 | if (index <= 1) {
320 | if (c == "\n") {
321 | response.openai = response.openai.slice(
322 | 1,
323 | response.openai.length
324 | );
325 | }
326 | } else {
327 | break;
328 | }
329 | index++;
330 | }
331 | response.db = await chat.Response(
332 | prompt,
333 | response,
334 | userId,
335 | chatId,
336 | assistant_id
337 | );
338 | }
339 | } else {
340 | response.openai = await openai.chat.completions.create({
341 | model: "gpt-4-0125-preview",
342 | messages: mes,
343 | top_p: 0.52,
344 | stream: true,
345 | });
346 | for await (const part of response.openai) {
347 | let text = part.choices[0].delta.content ?? "";
348 | full += text;
349 | }
350 | response.openai = {
351 | role: "assistant",
352 | content: full,
353 | };
354 | if (response.openai) {
355 | response.openai = response.openai.content;
356 | let index = 0;
357 | for (let c of response["openai"]) {
358 | if (index <= 1) {
359 | if (c == "\n") {
360 | response.openai = response.openai.slice(
361 | 1,
362 | response.openai.length
363 | );
364 | }
365 | } else {
366 | break;
367 | }
368 | index++;
369 | }
370 | response.db = await chat.Response(
371 | prompt,
372 | response,
373 | userId,
374 | chatId,
375 | assistant_id
376 | );
377 | }
378 | }
379 | } catch (err) {
380 | console.log(err);
381 | res.status(500).json({
382 | status: 500,
383 | message: err,
384 | });
385 | } finally {
386 | if (response?.db && response?.openai) {
387 | res.status(200).json({
388 | status: 200,
389 | message: "Success",
390 | data: {
391 | content: response.openai,
392 | chatId: response.db.chatId,
393 | },
394 | });
395 | }
396 | }
397 | });
398 |
399 | router.get("/saved", CheckUser, async (req, res) => {
400 | const { userId } = req.body;
401 | const { chatId = null } = req.query;
402 |
403 | let response = null;
404 |
405 | try {
406 | response = await chat.getChat(userId, chatId);
407 | } catch (err) {
408 | if (err?.status === 404) {
409 | res.status(404).json({
410 | status: 404,
411 | message: "Not found",
412 | });
413 | } else {
414 | res.status(500).json({
415 | status: 500,
416 | message: err,
417 | });
418 | }
419 | } finally {
420 | if (response) {
421 | res.status(200).json({
422 | status: 200,
423 | message: "Success",
424 | data: response,
425 | });
426 | }
427 | }
428 | });
429 |
430 | router.get("/history", CheckUser, async (req, res) => {
431 | const { userId } = req.body;
432 |
433 | let response = null;
434 |
435 | try {
436 | response = await chat.getHistory(userId);
437 | } catch (err) {
438 | res.status(500).json({
439 | status: 500,
440 | message: err,
441 | });
442 | } finally {
443 | if (response) {
444 | res.status(200).json({
445 | status: 200,
446 | message: "Success",
447 | data: response,
448 | });
449 | }
450 | }
451 | });
452 |
453 | router.delete("/all", CheckUser, async (req, res) => {
454 | const { userId } = req.body;
455 |
456 | let response = null;
457 |
458 | try {
459 | response = await chat.deleteAllChat(userId);
460 | } catch (err) {
461 | res.status(500).json({
462 | status: 500,
463 | message: err,
464 | });
465 | } finally {
466 | if (response) {
467 | res.status(200).json({
468 | status: 200,
469 | message: "Success",
470 | });
471 | }
472 | }
473 | });
474 |
475 | //Router for Attached Documnets Modal
476 |
477 | router.post("/getfile", async (req, res) => {
478 | const { userId, chatId } = req.body;
479 | let response = null;
480 |
481 | try {
482 | response = await chat.getFiles(userId, chatId);
483 | } catch (err) {
484 | res.status(500).json({
485 | status: 500,
486 | message: err,
487 | });
488 | } finally {
489 | if (response) {
490 | res.status(200).json({
491 | status: 200,
492 | message: "Success",
493 | data: response,
494 | });
495 | }
496 | }
497 | });
498 |
499 | router.post("/deletefile", CheckUser, async (req, res) => {
500 | const { userId, chatId, file_name } = req.body;
501 | let response = null;
502 |
503 | try {
504 | const file_id_obj = await db.collection(collections.CHAT).aggregate([
505 | {
506 | $match: {
507 | user: userId.toString(),
508 | },
509 | },
510 | {
511 | $unwind: "$data",
512 | },
513 | {
514 | $match: {
515 | "data.chatId": chatId,
516 | },
517 | },
518 | {
519 | $project: {
520 | data: 1,
521 | file_index: {
522 | $indexOfArray: ["$data.file_name", file_name]
523 | }
524 | }
525 | }
526 | ]).toArray();
527 | let file_id = file_id_obj[0]?.data?.files[file_id_obj[0]?.file_index];
528 | response = await chat.deleteFile(userId, chatId, file_name, file_id);
529 | } catch (err) {
530 | console.log(err);
531 | res.status(500).json({
532 | status: 500,
533 | message: err,
534 | });
535 | } finally {
536 | if (response) {
537 | res.status(200).json({
538 | status: 200,
539 | message: "Success",
540 | });
541 | }
542 | }
543 | });
544 |
545 | export default router;
546 |
--------------------------------------------------------------------------------
/client/src/components/menu/menu.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useEffect, useRef, useState } from "react";
2 | import {
3 | Avatar,
4 | Bar,
5 | LogOut,
6 | Message,
7 | Plus,
8 | Settings,
9 | Tab,
10 | Tick,
11 | Trash,
12 | Xicon,
13 | File,
14 | } from "../../assets/";
15 | import Profile from "../../assets/Profile";
16 | import { emptyUser } from "../../redux/user";
17 | import { useDispatch, useSelector } from "react-redux";
18 | import { useNavigate } from "react-router-dom";
19 | import { activePage, addHistory } from "../../redux/history";
20 | import ReactS3, { deleteFile } from "react-s3";
21 | import instance from "../../config/instance";
22 | import "./style.scss";
23 | import { Buffer } from "buffer";
24 | window.Buffer = Buffer;
25 | import { useContext } from "react";
26 | import { documentsContext } from "./../../App";
27 |
28 | const S3_BUCKET = "proton";
29 | const REGION = "us-east-1";
30 | const ACCESS_KEY = "access-key";
31 | const SECRET_ACCESS_KEY = "access-key";
32 |
33 | const config = {
34 | bucketName: S3_BUCKET,
35 | region: REGION,
36 | accessKeyId: ACCESS_KEY,
37 | secretAccessKey: SECRET_ACCESS_KEY,
38 | };
39 |
40 | const Menu = ({ changeColorMode }) => {
41 | let path = window.location.pathname;
42 | const user = useSelector((state) => state.user);
43 | let current_chat = useSelector((state) => state.history);
44 | current_chat = current_chat.filter((obj) => obj.active === true);
45 | const menuRef = useRef(null);
46 | const btnRef = useRef(null);
47 | const settingRef = useRef(null);
48 | const documentRef = useRef(null);
49 |
50 | const navigate = useNavigate();
51 | const dispatch = useDispatch();
52 |
53 | const { history } = useSelector((state) => state);
54 | const [confirm, setConfim] = useState(false);
55 | const [profilePicture, setProfilePicture] = useState(null);
56 | const [firstName, setFirstName] = useState("Anonymous ");
57 | const [lastName, setlastName] = useState("User");
58 | const { documents, getFiles } = useContext(documentsContext);
59 | const { _id } = useSelector(state => state.messages)
60 |
61 | const logOut = async () => {
62 | if (window.confirm("Do you want log out")) {
63 | let res = null;
64 | try {
65 | res = await instance.get("/api/user/logout");
66 | } catch (err) {
67 | alert(err);
68 | } finally {
69 | if (res?.data?.status === 200) {
70 | alert("Done");
71 | dispatch(emptyUser());
72 | navigate("/login");
73 | }
74 | }
75 | }
76 | };
77 |
78 | const clearHistory = async (del) => {
79 | if (del) {
80 | let res = null;
81 |
82 | try {
83 | res = instance.delete("/api/chat/all");
84 | } catch (err) {
85 | alert("Error");
86 | console.log(err);
87 | } finally {
88 | if (res) {
89 | navigate("/chat");
90 | dispatch(addHistory([]));
91 | }
92 |
93 | setConfim(false);
94 | }
95 | } else {
96 | setConfim(true);
97 | }
98 | };
99 |
100 | const showMenuMd = () => {
101 | menuRef.current.classList.add("showMd");
102 | document.body.style.overflowY = "hidden";
103 | };
104 |
105 | //Menu
106 |
107 | useEffect(() => {
108 | window.addEventListener("click", (e) => {
109 | if (
110 | !menuRef?.current?.contains(e.target) &&
111 | !btnRef?.current?.contains(e.target)
112 | ) {
113 | menuRef?.current?.classList?.remove("showMd");
114 | document.body.style.overflowY = "auto";
115 | }
116 | });
117 |
118 | window.addEventListener("resize", () => {
119 | if (!window.matchMedia("(max-width:767px)").matches) {
120 | document.body.style.overflowY = "auto";
121 | } else {
122 | if (menuRef?.current?.classList?.contains("showMd")) {
123 | document.body.style.overflowY = "hidden";
124 | } else {
125 | document.body.style.overflowY = "auto";
126 | }
127 | }
128 | });
129 | });
130 |
131 | const deleteFile = async (doc) => {
132 | let response = null;
133 | console.log(_id);
134 | try {
135 | console.log(_id);
136 | response = await instance.post("/api/chat/deletefile", {
137 | chatId: _id,
138 | file_name: doc,
139 | });
140 | console.log(response?.data);
141 | } catch (error) {
142 | console.log(error);
143 | } finally {
144 | if (response?.status === 200) {
145 | getFiles();
146 | } else {
147 | alert("File delete failed");
148 | }
149 | }
150 | };
151 |
152 | // History Get
153 | useEffect(() => {
154 | const getHistory = async () => {
155 | let res = null;
156 | try {
157 | res = await instance.get("/api/chat/history");
158 | } catch (err) {
159 | console.log(err);
160 | } finally {
161 | if (res?.data) {
162 | dispatch(addHistory(res?.data?.data));
163 | }
164 | }
165 | };
166 |
167 | getHistory();
168 | }, [path]);
169 |
170 | // History active
171 | useEffect(() => {
172 | setConfim(false);
173 | let chatId = path.replace("/chat/", "");
174 | chatId = chatId.replace("/", "");
175 | dispatch(activePage(chatId));
176 | }, [path, history]);
177 |
178 | return (
179 |
180 |
181 |
187 |
188 |
213 |
214 |
215 |
216 |
231 |
232 |
233 |
234 | {history?.map((obj, key) => {
235 | //console.log(obj)
236 | if (!obj?.chatId || obj.chat.length === 0) return null;
237 | if (obj?.active) {
238 | return (
239 |
249 | );
250 | } else {
251 | return (
252 |
261 | );
262 | }
263 | })}
264 |
265 |
266 |
267 |
279 | {history?.length > 0 && (
280 | <>
281 | {confirm ? (
282 |
286 | ) : (
287 |
291 | )}
292 | >
293 | )}
294 |
305 |
316 |
320 |
324 |
325 |
326 |
327 |
328 |
331 |
332 |
333 | );
334 | };
335 |
336 | export default Menu;
337 |
338 | const Modal = ({ changeColorMode, settingRef }) => {
339 | const dispatch = useDispatch();
340 | const navigate = useNavigate();
341 | const fileInputRef = useRef(null);
342 | const user = useSelector((state) => state.user);
343 | const updateUser = async () => {
344 | // Gather input values
345 | const firstName = document.getElementById("first-name").value;
346 | const lastName = document.getElementById("last-name").value;
347 | let profilePicture = fileInputRef.current.files[0]; // Get the selected file
348 | console.log(profilePicture);
349 | try {
350 | const data = await ReactS3.uploadFile(profilePicture, config);
351 | profilePicture = data.location;
352 | console.log("first name");
353 | // console.log(data.location)
354 | // Create FormData to send data
355 | const formData = new FormData();
356 | formData.append("email", user?.email);
357 | formData.append("firstName", firstName);
358 | formData.append("lastName", lastName);
359 | formData.append("profilePicture", profilePicture);
360 | // Make request to update user with FormData
361 | const response = await instance.post(
362 | "/api/user/update_profile",
363 | formData,
364 | {
365 | headers: {
366 | "Content-Type": "application/json", // Set content type to multipart/form-data
367 | },
368 | }
369 | );
370 |
371 | // Handle response here
372 | console.log("Update successful:", response.data);
373 | } catch (err) {
374 | console.error("Error updating user:", err);
375 | if (err?.response?.status === 405) {
376 | alert("Not Logged");
377 | dispatch(emptyUser());
378 | navigate("/login");
379 | } else {
380 | alert("An error occurred while updating user");
381 | }
382 | } finally {
383 | window.location.reload();
384 | }
385 | };
386 |
387 |
388 |
389 | const deleteAccount = async () => {
390 | if (window.confirm("Do you want delete your account")) {
391 | let res = null;
392 | try {
393 | res = await instance.delete("/api/user/account");
394 | } catch (err) {
395 | console.log(err);
396 | if (err?.response?.data?.status === 405) {
397 | alert("Not Logged");
398 | dispatch(emptyUser());
399 | navigate("/login");
400 | } else {
401 | alert(err);
402 | }
403 | } finally {
404 | alert("Success");
405 | dispatch(emptyUser());
406 | navigate("/login");
407 | }
408 | }
409 | };
410 |
411 | return (
412 | {
416 | let inner = settingRef.current.childNodes;
417 | if (!inner?.[0]?.contains(e.target)) {
418 | settingRef.current.style.display = "none";
419 | }
420 | }}
421 | >
422 |
423 |
427 |
Settings
428 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
Dark mode
450 |
464 |
465 |
466 |
475 |
476 |
477 |
478 |
481 |
482 |
483 |
484 | );
485 | };
486 |
487 | const DocumentModal = ({ changeColorMode, documentRef, documents, deleteFile }) => {
488 | return (
489 | {
493 | let inner = documentRef.current.childNodes;
494 | if (!inner?.[0]?.contains(e.target)) {
495 | documentRef.current.style.display = "none";
496 | }
497 | }}
498 | >
499 |
500 |
504 |
Attached Documents
505 |
512 |
513 |
514 | {documents &&
515 | documents.map((doc, index) => {
516 | return (
517 | doc && (
518 |
522 |
523 | {index}. {doc}
524 |
525 |
deleteFile(doc)}>
526 |
527 |
528 |
529 | )
530 | );
531 | })}
532 |
533 |
534 |
535 | );
536 | };
537 |
--------------------------------------------------------------------------------