├── .babelrc.js ├── .env ├── .env.dev ├── .gitignore ├── README.md ├── config-overrides.js ├── package.json ├── public ├── index.html ├── logo.svg ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── App.test.tsx ├── App.tsx ├── assets │ ├── avatar_01.svg │ ├── back_genie.png │ ├── back_graphics.svg │ ├── back_herograd.svg │ ├── back_heroicons.svg │ ├── back_product_1.png │ ├── back_product_2.png │ ├── back_product_3.png │ ├── back_product_4.png │ ├── back_section2.png │ ├── back_signin.png │ ├── back_signup.png │ ├── back_video.png │ ├── ico_anycode.svg │ ├── ico_apidoc.svg │ ├── ico_bug.svg │ ├── ico_bugfree.svg │ ├── ico_calendar.svg │ ├── ico_copy.svg │ ├── ico_crown.svg │ ├── ico_del.svg │ ├── ico_document.svg │ ├── ico_downarr.svg │ ├── ico_export.svg │ ├── ico_eye.svg │ ├── ico_facebook.svg │ ├── ico_github.svg │ ├── ico_google.svg │ ├── ico_image.svg │ ├── ico_leftarrow.svg │ ├── ico_linkedin.svg │ ├── ico_monolithic.svg │ ├── ico_play.svg │ ├── ico_plus.svg │ ├── ico_plus_white.svg │ ├── ico_product.svg │ ├── ico_refactor.svg │ ├── ico_review.svg │ ├── ico_rewrite.svg │ ├── ico_search.svg │ ├── ico_smartcon.svg │ ├── ico_suggest.svg │ ├── ico_testcase.svg │ ├── ico_text.svg │ ├── ico_tool.svg │ ├── ico_twtter.svg │ ├── ico_url.svg │ ├── ico_value.svg │ ├── ico_wish.svg │ ├── logo_corning.png │ ├── logo_hp.png │ ├── logo_nevada.png │ ├── logo_oracle.png │ ├── logo_peoplesoft.png │ └── logo_white.png ├── components │ ├── FAQItem.tsx │ ├── FullScreenLoader.tsx │ ├── Layout.tsx │ ├── MagicWand.tsx │ ├── Prompt.tsx │ ├── genie │ │ └── TotalPanel.tsx │ ├── headers │ │ ├── AdminHeader.tsx │ │ ├── Header.tsx │ │ └── LandingHeader.tsx │ └── requireUser.tsx ├── index.css ├── index.tsx ├── logo.svg ├── pages │ ├── admin │ │ ├── blogcreate.page.tsx │ │ ├── dashboard.page.tsx │ │ ├── price.page.tsx │ │ ├── product.page.tsx │ │ └── prompt.page.tsx │ ├── auth │ │ ├── forgot.page.tsx │ │ ├── signin.page.tsx │ │ ├── signup.page.tsx │ │ ├── terms.page.tsx │ │ └── verify.page.tsx │ ├── genie.page.tsx │ ├── landing.page.tsx │ ├── pricing.page.tsx │ └── unauthorized.page.tsx ├── react-app-env.d.ts ├── redux │ ├── api │ │ ├── authApi.ts │ │ ├── customFetchBase.ts │ │ ├── genieApi.ts │ │ ├── productApi.ts │ │ ├── promptApi.ts │ │ ├── types.ts │ │ └── userApi.ts │ ├── features │ │ ├── genieSlice.ts │ │ └── userSlice.ts │ └── store.ts ├── reportWebVitals.ts ├── setupTests.ts └── theme.ts ├── tsconfig.json └── yarn.lock /.babelrc.js: -------------------------------------------------------------------------------- 1 | const plugins = [ 2 | [ 3 | "babel-plugin-import", 4 | { 5 | libraryName: "@mui/material", 6 | libraryDirectory: "", 7 | camel2DashComponentName: false, 8 | }, 9 | "core", 10 | ], 11 | [ 12 | "babel-plugin-import", 13 | { 14 | libraryName: "@mui/icons-material", 15 | libraryDirectory: "", 16 | camel2DashComponentName: false, 17 | }, 18 | "icons", 19 | ], 20 | ]; 21 | 22 | module.exports = { plugins }; 23 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | REACT_APP_SERVER_ENDPOINT=https://backend.code-genie.ai 2 | REACT_APP_GG_APP_ID=531434323354-h4nohoohiuhefrj0cigr4rratflf1ngm.apps.googleusercontent.com 3 | REACT_APP_GITHUB_APP_ID=769c7da805be050a5d7d 4 | REACT_APP_GITHUB_APP_SECRET=ff0549dbf72cd345e928a1ee0a6bb6e836e7fe78 -------------------------------------------------------------------------------- /.env.dev: -------------------------------------------------------------------------------- 1 | REACT_APP_SERVER_ENDPOINT=http://localhost:8000 2 | REACT_APP_GG_APP_ID=531434323354-h4nohoohiuhefrj0cigr4rratflf1ngm.apps.googleusercontent.com 3 | REACT_APP_GITHUB_APP_ID=769c7da805be050a5d7d 4 | REACT_APP_GITHUB_APP_SECRET=ff0549dbf72cd345e928a1ee0a6bb6e836e7fe78 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `yarn start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `yarn test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `yarn build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `yarn eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | -------------------------------------------------------------------------------- /config-overrides.js: -------------------------------------------------------------------------------- 1 | /* config-overrides.js */ 2 | /* eslint-disable react-hooks/rules-of-hooks */ 3 | const { useBabelRc, override } = require("customize-cra"); 4 | 5 | module.exports = override(useBabelRc()); 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "codegenie", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@emotion/react": "^11.11.1", 7 | "@emotion/styled": "^11.11.0", 8 | "@hookform/resolvers": "^3.1.1", 9 | "@mui/icons-material": "^5.14.1", 10 | "@mui/lab": "^5.0.0-alpha.137", 11 | "@mui/material": "^5.14.1", 12 | "@mui/styled-engine-sc": "^5.12.0", 13 | "@reduxjs/toolkit": "^1.9.5", 14 | "@testing-library/jest-dom": "^5.14.1", 15 | "@testing-library/react": "^13.0.0", 16 | "@testing-library/user-event": "^13.2.1", 17 | "@types/jest": "^27.0.1", 18 | "@types/node": "^16.7.13", 19 | "@types/react": "^18.0.0", 20 | "@types/react-dom": "^18.0.0", 21 | "@uiw/react-textarea-code-editor": "^2.1.7", 22 | "async-mutex": "^0.4.0", 23 | "axios": "^1.4.0", 24 | "react": "^18.2.0", 25 | "react-cookie": "^4.1.1", 26 | "react-dom": "^18.2.0", 27 | "react-hook-form": "^7.45.2", 28 | "react-redux": "^8.1.1", 29 | "react-router-dom": "^6.14.2", 30 | "react-scripts": "5.0.1", 31 | "react-toastify": "^9.1.3", 32 | "reactjs-social-login": "^2.6.2", 33 | "styled-components": "^6.0.5", 34 | "typescript": "^4.4.2", 35 | "web-vitals": "^2.1.0", 36 | "zod": "^3.21.4" 37 | }, 38 | "scripts": { 39 | "start": "react-app-rewired start", 40 | "build": "react-app-rewired build", 41 | "test": "react-app-rewired test", 42 | "eject": "react-scripts eject" 43 | }, 44 | "eslintConfig": { 45 | "extends": [ 46 | "react-app", 47 | "react-app/jest" 48 | ] 49 | }, 50 | "browserslist": { 51 | "production": [ 52 | ">0.2%", 53 | "not dead", 54 | "not op_mini all" 55 | ], 56 | "development": [ 57 | "last 1 chrome version", 58 | "last 1 firefox version", 59 | "last 1 safari version" 60 | ] 61 | }, 62 | "devDependencies": { 63 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11", 64 | "babel-plugin-import": "^1.13.6", 65 | "customize-cra": "^1.0.0", 66 | "react-app-rewired": "^2.2.1" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | Code Genie 18 | 36 | 40 | 48 | 49 | 50 | 51 | 54 |
55 | 56 | 57 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softdev629/codegenie-react/6f44dbaabb983ee9a7989ac2f307932093e9ece3/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softdev629/codegenie-react/6f44dbaabb983ee9a7989ac2f307932093e9ece3/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | render(); 7 | const linkElement = screen.getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useMemo } from "react"; 2 | import { CssBaseline } from "@mui/material"; 3 | import { Routes, Route } from "react-router-dom"; 4 | import { PaletteMode } from "@mui/material"; 5 | import { createTheme, ThemeProvider } from "@mui/material/styles"; 6 | 7 | import "react-toastify/dist/ReactToastify.css"; 8 | 9 | import { ToastContainer } from "react-toastify"; 10 | import Layout from "./components/Layout"; 11 | import { getDesignTokens, ColorModeContext } from "./theme"; 12 | import ProductConfigurator from "./pages/admin/product.page"; 13 | import PriceConfigurator from "./pages/admin/price.page"; 14 | import PromptConfigurator from "./pages/admin/prompt.page"; 15 | import Dashboard from "./pages/admin/dashboard.page"; 16 | import ContentGenerator from "./pages/admin/blogcreate.page"; 17 | import GeniePage from "./pages/genie.page"; 18 | import LandingPage from "./pages/landing.page"; 19 | import SignupPage from "./pages/auth/signup.page"; 20 | import TermsPage from "./pages/auth/terms.page"; 21 | import SigninPage from "./pages/auth/signin.page"; 22 | import ForgotPage from "./pages/auth/forgot.page"; 23 | import VerifyPage from "./pages/auth/verify.page"; 24 | import RequireUser from "./components/requireUser"; 25 | import UnauthorizePage from "./pages/unauthorized.page"; 26 | import PricingPage from "./pages/pricing.page"; 27 | 28 | function App() { 29 | const [mode, setMode] = useState("light"); 30 | const colorMode = useMemo( 31 | () => ({ 32 | toggleColorMode: () => { 33 | setMode((prevMode: PaletteMode) => 34 | prevMode === "light" ? "dark" : "light" 35 | ); 36 | }, 37 | }), 38 | [] 39 | ); 40 | const theme = useMemo(() => createTheme(getDesignTokens(mode)), [mode]); 41 | 42 | return ( 43 | <> 44 | 45 | 46 | 47 | 48 | 49 | } /> 50 | }> 51 | }> 52 | } /> 53 | } /> 54 | 55 | 56 | }> 57 | }> 58 | } /> 59 | 60 | } /> 61 | } /> 62 | } /> 63 | 64 | 65 | } /> 66 | 67 | 68 | 69 | } /> 70 | } /> 71 | } /> 72 | } /> 73 | } /> 74 | } /> 75 | 76 | 77 | 78 | 79 | ); 80 | } 81 | 82 | export default App; 83 | -------------------------------------------------------------------------------- /src/assets/back_genie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softdev629/codegenie-react/6f44dbaabb983ee9a7989ac2f307932093e9ece3/src/assets/back_genie.png -------------------------------------------------------------------------------- /src/assets/back_graphics.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/assets/back_herograd.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/assets/back_heroicons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /src/assets/back_product_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softdev629/codegenie-react/6f44dbaabb983ee9a7989ac2f307932093e9ece3/src/assets/back_product_1.png -------------------------------------------------------------------------------- /src/assets/back_product_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softdev629/codegenie-react/6f44dbaabb983ee9a7989ac2f307932093e9ece3/src/assets/back_product_2.png -------------------------------------------------------------------------------- /src/assets/back_product_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softdev629/codegenie-react/6f44dbaabb983ee9a7989ac2f307932093e9ece3/src/assets/back_product_3.png -------------------------------------------------------------------------------- /src/assets/back_product_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softdev629/codegenie-react/6f44dbaabb983ee9a7989ac2f307932093e9ece3/src/assets/back_product_4.png -------------------------------------------------------------------------------- /src/assets/back_section2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softdev629/codegenie-react/6f44dbaabb983ee9a7989ac2f307932093e9ece3/src/assets/back_section2.png -------------------------------------------------------------------------------- /src/assets/back_signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softdev629/codegenie-react/6f44dbaabb983ee9a7989ac2f307932093e9ece3/src/assets/back_signin.png -------------------------------------------------------------------------------- /src/assets/back_signup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softdev629/codegenie-react/6f44dbaabb983ee9a7989ac2f307932093e9ece3/src/assets/back_signup.png -------------------------------------------------------------------------------- /src/assets/back_video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softdev629/codegenie-react/6f44dbaabb983ee9a7989ac2f307932093e9ece3/src/assets/back_video.png -------------------------------------------------------------------------------- /src/assets/ico_anycode.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/ico_apidoc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/ico_bug.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/ico_bugfree.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/ico_calendar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/ico_copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/ico_crown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/ico_del.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/ico_document.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/assets/ico_downarr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/ico_export.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/ico_eye.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/ico_facebook.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/ico_github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/ico_google.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/ico_image.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/ico_leftarrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/ico_linkedin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/ico_monolithic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/ico_play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/ico_plus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/ico_plus_white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/ico_product.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/ico_refactor.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/assets/ico_review.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/ico_rewrite.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/ico_search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/ico_smartcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/assets/ico_suggest.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/ico_testcase.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/ico_text.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/ico_tool.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/ico_twtter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/ico_url.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/ico_value.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/ico_wish.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/logo_corning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softdev629/codegenie-react/6f44dbaabb983ee9a7989ac2f307932093e9ece3/src/assets/logo_corning.png -------------------------------------------------------------------------------- /src/assets/logo_hp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softdev629/codegenie-react/6f44dbaabb983ee9a7989ac2f307932093e9ece3/src/assets/logo_hp.png -------------------------------------------------------------------------------- /src/assets/logo_nevada.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softdev629/codegenie-react/6f44dbaabb983ee9a7989ac2f307932093e9ece3/src/assets/logo_nevada.png -------------------------------------------------------------------------------- /src/assets/logo_oracle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softdev629/codegenie-react/6f44dbaabb983ee9a7989ac2f307932093e9ece3/src/assets/logo_oracle.png -------------------------------------------------------------------------------- /src/assets/logo_peoplesoft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softdev629/codegenie-react/6f44dbaabb983ee9a7989ac2f307932093e9ece3/src/assets/logo_peoplesoft.png -------------------------------------------------------------------------------- /src/assets/logo_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softdev629/codegenie-react/6f44dbaabb983ee9a7989ac2f307932093e9ece3/src/assets/logo_white.png -------------------------------------------------------------------------------- /src/components/FAQItem.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { Typography, Stack, IconButton, Collapse, Box } from "@mui/material"; 3 | import { KeyboardArrowUp, KeyboardArrowDown } from "@mui/icons-material"; 4 | 5 | const FAQItem = (props: { title: string; content: string }) => { 6 | const [open, setOpen] = useState(false); 7 | return ( 8 | <> 9 | 10 | 11 | 17 | {props.title} 18 | 19 | setOpen(!open)}> 20 | {open ? : } 21 | 22 | 23 | 24 | {props.content} 25 | 26 | 27 | 28 | ); 29 | }; 30 | 31 | export default FAQItem; 32 | -------------------------------------------------------------------------------- /src/components/FullScreenLoader.tsx: -------------------------------------------------------------------------------- 1 | import { Box, CircularProgress, Container } from "@mui/material"; 2 | 3 | const FullScreenLoader = () => { 4 | return ( 5 | 6 | 12 | 13 | 14 | 15 | ); 16 | }; 17 | 18 | export default FullScreenLoader; 19 | -------------------------------------------------------------------------------- /src/components/Layout.tsx: -------------------------------------------------------------------------------- 1 | import { Outlet } from "react-router-dom"; 2 | import Header from "./headers/Header"; 3 | import AdminHeader from "./headers/AdminHeader"; 4 | 5 | const Layout = (props: { type: "admin" | "user" }) => { 6 | return ( 7 | <> 8 | {props.type === "admin" ? :
} 9 | 10 | 11 | ); 12 | }; 13 | 14 | export default Layout; 15 | -------------------------------------------------------------------------------- /src/components/MagicWand.tsx: -------------------------------------------------------------------------------- 1 | const MagicWand = () => { 2 | return ( 3 | <> 4 |
5 | 14 | spinner 15 | 16 | 21 | 22 | 27 | 28 | 33 | 34 | 39 | 40 | 45 | 46 | 51 | 52 |
53 |
54 | 55 | ); 56 | }; 57 | 58 | export default MagicWand; 59 | -------------------------------------------------------------------------------- /src/components/Prompt.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { Box, Stack, Typography, TextField, MenuItem } from "@mui/material"; 3 | import { useForm, FormProvider, SubmitHandler } from "react-hook-form"; 4 | import { object, string, TypeOf, number } from "zod"; 5 | import { zodResolver } from "@hookform/resolvers/zod"; 6 | import { LoadingButton } from "@mui/lab"; 7 | import { toast } from "react-toastify"; 8 | 9 | import { 10 | useLazyGetModulesQuery, 11 | useLazyGetPricesQuery, 12 | } from "../redux/api/productApi"; 13 | import { 14 | useAddPromptMutation, 15 | useUpdatePromptMutation, 16 | } from "../redux/api/promptApi"; 17 | 18 | const promptSchema = object({ 19 | product: string(), 20 | plan: string().optional(), 21 | module: string(), 22 | prompt_name: string(), 23 | order: number(), 24 | prompt: string(), 25 | }); 26 | 27 | export type IPromptSchema = TypeOf; 28 | 29 | const Prompt = (props: { 30 | id: string; 31 | product: string; 32 | plan: string; 33 | module: string; 34 | prompt_name: string; 35 | order: number; 36 | prompt: string; 37 | products: string[]; 38 | }) => { 39 | const [plans, setPlans] = useState([props.plan]); 40 | const [modules, setModules] = useState([props.module]); 41 | 42 | const [getModules, modulesState] = useLazyGetModulesQuery(); 43 | const [getPrices, pricesState] = useLazyGetPricesQuery(); 44 | const [addPrompt, addState] = useAddPromptMutation(); 45 | const [updatePrompt, updateState] = useUpdatePromptMutation(); 46 | 47 | const methods = useForm({ 48 | resolver: zodResolver(promptSchema), 49 | defaultValues: { 50 | product: props.product, 51 | plan: props.plan, 52 | module: props.module, 53 | prompt_name: props.prompt_name, 54 | order: props.order, 55 | prompt: props.prompt, 56 | }, 57 | }); 58 | 59 | const { handleSubmit, getValues, register, setValue } = methods; 60 | 61 | useEffect(() => { 62 | if (props.product !== "") { 63 | getModules(props.product); 64 | getPrices({ product_name: props.product, product_module: props.module }); 65 | } 66 | 67 | // eslint-disable-next-line react-hooks/exhaustive-deps 68 | }, []); 69 | 70 | useEffect(() => { 71 | if (modulesState.isSuccess) { 72 | setModules(modulesState.data); 73 | } 74 | }, [modulesState]); 75 | 76 | useEffect(() => { 77 | if (pricesState.isSuccess) { 78 | if (pricesState.data) 79 | setPlans(pricesState.data.map((item) => item.plan_name)); 80 | else { 81 | setPlans([""]); 82 | setValue("plan", ""); 83 | } 84 | } 85 | // eslint-disable-next-line react-hooks/exhaustive-deps 86 | }, [pricesState]); 87 | 88 | useEffect(() => { 89 | if (addState.isSuccess) toast.success("Prompt added successfully."); 90 | }, [addState]); 91 | 92 | useEffect(() => { 93 | if (updateState.isSuccess) toast.success("Prompt updated successfully."); 94 | }, [updateState]); 95 | 96 | const onSubmitHandler: SubmitHandler = ( 97 | values: IPromptSchema 98 | ) => { 99 | if (props.id === "") addPrompt(values); 100 | else updatePrompt({ id: props.id, info: values }); 101 | }; 102 | 103 | return ( 104 | <> 105 | 106 | 114 | 115 | 116 | Save 117 | 118 | 119 | 120 | Products 121 | Plans 122 | Module 123 | Prompt name 124 | Feature list order 125 | 126 | 127 | { 134 | getModules(event.target.value); 135 | }} 136 | > 137 | {props.products.map((product, index) => ( 138 | 139 | {product} 140 | 141 | ))} 142 | 143 | 150 | {plans.map((plan, index) => ( 151 | 152 | {plan} 153 | 154 | ))} 155 | 156 | { 163 | let product_name = getValues("product"); 164 | getPrices({ 165 | product_name: product_name, 166 | product_module: event.target.value, 167 | }); 168 | }} 169 | > 170 | {modules.map((module, index) => ( 171 | 172 | {module} 173 | 174 | ))} 175 | 176 | 182 | 189 | 190 | 191 | Note: 3rd listed in UI 192 | 193 | Prompt 194 | 201 | 202 | 203 | 204 | ); 205 | }; 206 | 207 | export default Prompt; 208 | -------------------------------------------------------------------------------- /src/components/genie/TotalPanel.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { 3 | Box, 4 | Stack, 5 | Typography, 6 | SvgIcon, 7 | IconButton, 8 | Button, 9 | Divider, 10 | Tabs, 11 | Tab, 12 | Menu, 13 | MenuItem, 14 | } from "@mui/material"; 15 | 16 | import { ReactComponent as CopyIcon } from "../../assets/ico_copy.svg"; 17 | import { ReactComponent as ExportIcon } from "../../assets/ico_export.svg"; 18 | 19 | import { useGetPromptNamesQuery } from "../../redux/api/promptApi"; 20 | import { useAppDispatch, useAppSelector } from "../../redux/store"; 21 | import { setPromptName } from "../../redux/features/genieSlice"; 22 | import { useExportFileMutation } from "../../redux/api/genieApi"; 23 | 24 | interface TabPanelProps { 25 | children?: React.ReactNode; 26 | value: number; 27 | } 28 | 29 | function TabPanel(props: TabPanelProps) { 30 | const { children, value, ...other } = props; 31 | 32 | return ( 33 |
34 | ({ 36 | p: 3, 37 | backgroundColor: theme.palette.background.paper, 38 | height: "100%", 39 | })} 40 | > 41 | {children} 42 | 43 |
44 | ); 45 | } 46 | 47 | function a11yProps(index: number) { 48 | return { 49 | id: `vertical-tab-${index}`, 50 | "aria-controls": `vertical-tabpanel-${index}`, 51 | }; 52 | } 53 | 54 | export default function TotalPanel({ 55 | export_check, 56 | export_text, 57 | export_pdf, 58 | export_word, 59 | }: { 60 | export_check: string[]; 61 | export_text: string; 62 | export_pdf: string; 63 | export_word: string; 64 | }) { 65 | const [value, setValue] = useState(0); 66 | const [anchorEl, setAnchorEl] = useState(null); 67 | 68 | const genieSelector = useAppSelector((state) => state.genieState); 69 | const dispatch = useAppDispatch(); 70 | 71 | const [exportFile, exportState] = useExportFileMutation(); 72 | 73 | const promptsInfo = useGetPromptNamesQuery({ 74 | product_name: "CodeGenie", 75 | product_module: genieSelector.module ? genieSelector.module : "", 76 | }); 77 | 78 | useEffect(() => { 79 | if (promptsInfo.isSuccess) { 80 | dispatch(setPromptName(promptsInfo.data[0])); 81 | } 82 | // eslint-disable-next-line react-hooks/exhaustive-deps 83 | }, [promptsInfo]); 84 | 85 | useEffect(() => { 86 | if (exportState.isSuccess) { 87 | fetch( 88 | `${process.env.REACT_APP_SERVER_ENDPOINT}/static/${exportState.data.path}` 89 | ) 90 | .then((response) => response.blob()) 91 | .then((blob) => { 92 | const url = window.URL.createObjectURL(blob); 93 | const link = document.createElement("a"); 94 | link.href = url; 95 | link.setAttribute("download", `${exportState.data.path}`); 96 | document.body.appendChild(link); 97 | link.click(); 98 | }); 99 | } 100 | }, [exportState]); 101 | 102 | const handleChange = (event: React.SyntheticEvent, newValue: number) => { 103 | setValue(newValue); 104 | if (promptsInfo.data) dispatch(setPromptName(promptsInfo.data[newValue])); 105 | }; 106 | 107 | if (!promptsInfo.data || promptsInfo.isLoading || promptsInfo.isFetching) 108 | return ( 109 | ({ 111 | flexGrow: 1, 112 | display: "flex", 113 | height: "100%", 114 | 115 | border: "1px solid", 116 | borderColor: theme.palette.divider, 117 | borderRadius: 1, 118 | })} 119 | > 120 | ); 121 | 122 | return ( 123 | ({ 125 | flexGrow: 1, 126 | display: "flex", 127 | height: "100%", 128 | 129 | border: "1px solid", 130 | borderColor: theme.palette.divider, 131 | borderRadius: 1, 132 | })} 133 | > 134 | ({ 139 | borderRight: 1, 140 | borderColor: "divider", 141 | overflow: "visible", 142 | backgroundColor: 143 | theme.palette.mode === "dark" 144 | ? theme.palette.background.default 145 | : "white", 146 | ".MuiTabs-indicator": { 147 | visibility: "hidden", 148 | }, 149 | ".MuiTabs-scroller": { 150 | overflow: "visible !important", 151 | }, 152 | })} 153 | > 154 | {promptsInfo.data.map((panel, index) => ( 155 | ({ 158 | textTransform: "none", 159 | alignItems: "flex-start", 160 | color: theme.palette.mode === "dark" ? "#F8FAFC" : "#475569", 161 | overflow: "visible", 162 | padding: "24px 14px", 163 | borderBottom: "1px solid", 164 | borderColor: "divider", 165 | "&.Mui-selected": { 166 | borderLeft: "3px solid", 167 | backgroundColor: theme.palette.background.paper, 168 | }, 169 | "&.Mui-selected::after": { 170 | overflow: "visible", 171 | content: '" "', 172 | left: "98%", 173 | position: "absolute", 174 | width: 10, 175 | height: "100%", 176 | zIndex: 101, 177 | backgroundColor: theme.palette.background.paper, 178 | }, 179 | })} 180 | label={panel} 181 | {...a11yProps(index)} 182 | /> 183 | ))} 184 | 185 | 186 | 187 | 188 | 189 | 195 | Results 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 220 | setAnchorEl(null)} 224 | > 225 | {export_check.includes("export_pdf") && ( 226 | { 228 | exportFile({ 229 | doc_type: "pdf", 230 | advice: genieSelector.msg, 231 | }); 232 | }} 233 | > 234 | {export_pdf} 235 | 236 | )} 237 | {export_check.includes("export_word") && ( 238 | { 240 | exportFile({ 241 | doc_type: "docx", 242 | advice: genieSelector.msg, 243 | }); 244 | }} 245 | > 246 | {export_word} 247 | 248 | )} 249 | {export_check.includes("export_text") && ( 250 | { 252 | exportFile({ 253 | doc_type: "txt", 254 | advice: genieSelector.msg, 255 | }); 256 | }} 257 | > 258 | {export_text} 259 | 260 | )} 261 | 262 | 263 | 264 | 265 | ({ 268 | whiteSpace: "pre-wrap", 269 | overflow: "auto", 270 | height: "calc(100vh - 400px)", 271 | "&.MuiBox-root::-webkit-scrollbar": { 272 | width: 4, 273 | }, 274 | "&.MuiBox-root::-webkit-scrollbar-thumb": { 275 | backgroundColor: theme.palette.divider, 276 | borderRadius: 4, 277 | }, 278 | "&.MuiBox-root::-webkit-scrollbar-track": { 279 | backgroundColor: 280 | theme.palette.mode === "dark" ? "#2D2D2D" : "#fff", 281 | }, 282 | })} 283 | > 284 | { 285 | genieSelector.msg[ 286 | genieSelector.prompt_name ? genieSelector.prompt_name : "" 287 | ] 288 | } 289 | 290 | 291 | 292 | 293 | ); 294 | } 295 | -------------------------------------------------------------------------------- /src/components/headers/AdminHeader.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { useNavigate } from "react-router-dom"; 3 | import { 4 | Box, 5 | SvgIcon, 6 | Typography, 7 | Stack, 8 | Divider, 9 | Tabs, 10 | Tab, 11 | } from "@mui/material"; 12 | 13 | import { ReactComponent as Logo } from "../../logo.svg"; 14 | 15 | const pages = [ 16 | { 17 | text: "Dashboard", 18 | link: "/dashboard", 19 | }, 20 | { 21 | text: "Content Generator", 22 | link: "/content/generate", 23 | }, 24 | { 25 | text: "Content Editor", 26 | link: "/content/edit", 27 | }, 28 | { 29 | text: "Price Configurator", 30 | link: "/config/prices", 31 | }, 32 | 33 | { 34 | text: "API Configurator", 35 | link: "/config/api", 36 | }, 37 | { 38 | text: "Prompt Configurator", 39 | link: "/config/prompts", 40 | }, 41 | { 42 | text: "Product Configurator", 43 | link: "/config/products", 44 | }, 45 | ]; 46 | 47 | const AdminHeader = () => { 48 | const [value, setValue] = useState(0); 49 | const navigate = useNavigate(); 50 | return ( 51 | <> 52 | 53 | 54 | 55 | 62 | 63 | 64 | 78 | CodeGenie 79 | 80 | 81 | 83 | setValue(newValue) 84 | } 85 | value={value} 86 | > 87 | {pages.map((page, index) => ( 88 | navigate(`/admin${page.link}`)} 97 | /> 98 | ))} 99 | 100 | 101 | 102 | 103 | 104 | ); 105 | }; 106 | 107 | export default AdminHeader; 108 | -------------------------------------------------------------------------------- /src/components/headers/Header.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useContext, useEffect } from "react"; 2 | import { 3 | Box, 4 | Toolbar, 5 | IconButton, 6 | Typography, 7 | Menu, 8 | Container, 9 | Button, 10 | MenuItem, 11 | SvgIcon, 12 | FormControlLabel, 13 | Switch, 14 | Divider, 15 | } from "@mui/material"; 16 | import { Menu as MenuIcon } from "@mui/icons-material"; 17 | import { styled } from "@mui/material/styles"; 18 | import { useNavigate } from "react-router-dom"; 19 | 20 | import { ReactComponent as Logo } from "../../logo.svg"; 21 | import { ReactComponent as CrownIcon } from "../../assets/ico_crown.svg"; 22 | import { ReactComponent as ProductIcon } from "../../assets/ico_product.svg"; 23 | import { ReactComponent as DownIcon } from "../../assets/ico_downarr.svg"; 24 | 25 | import { ColorModeContext } from "../../theme"; 26 | import { useLazyGetModulesQuery } from "../../redux/api/productApi"; 27 | import { useAppDispatch } from "../../redux/store"; 28 | import { setModule } from "../../redux/features/genieSlice"; 29 | 30 | const AntSwitch = styled(Switch)(({ theme }) => ({ 31 | width: 32, 32 | height: 16, 33 | padding: 0, 34 | marginLeft: 10, 35 | border: "1px solid #1D252D", 36 | borderRadius: 8, 37 | display: "flex", 38 | "&:active": { 39 | "& .MuiSwitch-thumb": { 40 | width: 15, 41 | }, 42 | "& .MuiSwitch-switchBase.Mui-checked": { 43 | transform: "translateX(9px)", 44 | }, 45 | }, 46 | "& .MuiSwitch-switchBase": { 47 | padding: 2, 48 | "&.Mui-checked": { 49 | transform: "translateX(16px)", 50 | color: "#fff", 51 | "& + .MuiSwitch-track": { 52 | opacity: 1, 53 | backgroundColor: theme.palette.mode === "dark" ? "#177ddc" : "#1890ff", 54 | }, 55 | }, 56 | }, 57 | "& .MuiSwitch-thumb": { 58 | boxShadow: "0 2px 4px 0 rgb(0 35 11 / 20%)", 59 | width: 10, 60 | height: 10, 61 | borderRadius: 6, 62 | transition: theme.transitions.create(["width"], { 63 | duration: 200, 64 | }), 65 | background: theme.palette.mode === "dark" ? "white" : "#1D252D", 66 | }, 67 | "& .MuiSwitch-track": { 68 | borderRadius: 16 / 2, 69 | opacity: 1, 70 | backgroundColor: 71 | theme.palette.mode === "dark" 72 | ? "linear-gradient(#0168B5, #4BA5EB)" 73 | : "rgba(255,255,255,1)", 74 | boxSizing: "border-box", 75 | }, 76 | })); 77 | 78 | function Header() { 79 | const [anchorElNav, setAnchorElNav] = useState(null); 80 | const colorMode = useContext(ColorModeContext); 81 | 82 | const [modules, setModules] = useState([]); 83 | const [anchorEl, setAnchorEl] = useState(null); 84 | 85 | const navigate = useNavigate(); 86 | const dispatch = useAppDispatch(); 87 | 88 | const open = Boolean(anchorEl); 89 | const handleClick = (event: React.MouseEvent) => { 90 | setAnchorEl(event.currentTarget); 91 | }; 92 | const handleClose = () => { 93 | setAnchorEl(null); 94 | }; 95 | 96 | const [getModules, getState] = useLazyGetModulesQuery(); 97 | 98 | useEffect(() => { 99 | getModules("CodeGenie"); 100 | // eslint-disable-next-line react-hooks/exhaustive-deps 101 | }, []); 102 | 103 | useEffect(() => { 104 | if (getState.isSuccess) 105 | setModules(getState.data.filter((item) => item !== "")); 106 | }, [getState]); 107 | 108 | const handleOpenNavMenu = (event: React.MouseEvent) => { 109 | setAnchorElNav(event.currentTarget); 110 | }; 111 | 112 | const handleCloseNavMenu = () => { 113 | setAnchorElNav(null); 114 | }; 115 | 116 | return ( 117 | <> 118 | 119 | 120 | 127 | 128 | 129 | { 142 | event.preventDefault(); 143 | navigate("/"); 144 | }} 145 | > 146 | CodeGenie 147 | 148 | 149 | 163 | CodeGenie 164 | 165 | 166 | 174 | 175 | 176 | 194 | 195 | 203 | 223 | 232 | {modules.map((module, index) => ( 233 | { 236 | handleClose(); 237 | navigate( 238 | `codegenie/${module.toLowerCase().replace(" ", "_")}` 239 | ); 240 | localStorage.setItem("module", module); 241 | dispatch(setModule(module)); 242 | }} 243 | > 244 | {module} 245 | 246 | ))} 247 | 248 | 251 | 252 | 253 | 254 | 255 | 256 | } 259 | label="Darkmode" 260 | labelPlacement="start" 261 | /> 262 | 278 | 279 | 280 | 281 | 282 | 283 | ); 284 | } 285 | export default Header; 286 | -------------------------------------------------------------------------------- /src/components/headers/LandingHeader.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { 3 | Container, 4 | Toolbar, 5 | SvgIcon, 6 | Typography, 7 | Box, 8 | IconButton, 9 | Menu, 10 | Button, 11 | Divider, 12 | MenuItem, 13 | } from "@mui/material"; 14 | import { Menu as MenuIcon } from "@mui/icons-material"; 15 | 16 | import { ReactComponent as Logo } from "../../logo.svg"; 17 | import { ReactComponent as ProductIcon } from "../../assets/ico_product.svg"; 18 | import { ReactComponent as DownIcon } from "../../assets/ico_downarr.svg"; 19 | 20 | import { useLazyGetModulesQuery } from "../../redux/api/productApi"; 21 | import { setModule } from "../../redux/features/genieSlice"; 22 | import { useAppDispatch } from "../../redux/store"; 23 | import { useNavigate } from "react-router-dom"; 24 | 25 | const LandingHeader = () => { 26 | const [anchorElNav, setAnchorElNav] = useState(null); 27 | const [anchorEl, setAnchorEl] = useState(null); 28 | const [modules, setModules] = useState([]); 29 | 30 | const [getModules, getState] = useLazyGetModulesQuery(); 31 | 32 | const dispatch = useAppDispatch(); 33 | const navigate = useNavigate(); 34 | 35 | useEffect(() => { 36 | getModules("CodeGenie"); 37 | // eslint-disable-next-line react-hooks/exhaustive-deps 38 | }, []); 39 | 40 | useEffect(() => { 41 | if (getState.isSuccess) { 42 | setModules(getState.data.filter((item) => item !== "")); 43 | dispatch(setModule(getState.data[0])); 44 | } 45 | // eslint-disable-next-line react-hooks/exhaustive-deps 46 | }, [getState]); 47 | 48 | const open = Boolean(anchorEl); 49 | 50 | const handleClick = (event: React.MouseEvent) => { 51 | setAnchorEl(event.currentTarget); 52 | }; 53 | const handleClose = () => { 54 | setAnchorEl(null); 55 | }; 56 | 57 | const handleOpenNavMenu = (event: React.MouseEvent) => { 58 | setAnchorElNav(event.currentTarget); 59 | }; 60 | 61 | const handleCloseNavMenu = () => { 62 | setAnchorElNav(null); 63 | }; 64 | 65 | return ( 66 | <> 67 | 68 | 69 | 76 | 77 | 78 | 91 | CodeGenie 92 | 93 | 94 | 108 | CodeGenie 109 | 110 | 111 | 119 | 120 | 121 | 139 | 140 | 148 | 168 | 177 | {modules.map((module, index) => ( 178 | { 181 | handleClose(); 182 | localStorage.setItem("module", module); 183 | dispatch(setModule(module)); 184 | }} 185 | > 186 | {module} 187 | 188 | ))} 189 | 190 | 191 | 192 | 207 | 208 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | ); 227 | }; 228 | 229 | export default LandingHeader; 230 | -------------------------------------------------------------------------------- /src/components/requireUser.tsx: -------------------------------------------------------------------------------- 1 | import { useCookies } from "react-cookie"; 2 | import { Navigate, Outlet, useLocation } from "react-router-dom"; 3 | import { userApi } from "../redux/api/userApi"; 4 | import FullScreenLoader from "./FullScreenLoader"; 5 | 6 | const RequireUser = ({ allowedRoles }: { allowedRoles: string[] }) => { 7 | const [cookies] = useCookies(["logged_in"]); 8 | const location = useLocation(); 9 | 10 | const { isLoading, isFetching } = userApi.endpoints.getMe.useQuery(null, { 11 | skip: false, 12 | refetchOnMountOrArgChange: true, 13 | }); 14 | 15 | const loading = isLoading || isFetching; 16 | 17 | const userData = userApi.endpoints.getMe.useQueryState(null); 18 | 19 | if (loading) { 20 | return ; 21 | } 22 | 23 | return (cookies.logged_in || userData) && 24 | allowedRoles.includes(userData?.data?.role as string) ? ( 25 | 26 | ) : cookies.logged_in && userData ? ( 27 | 28 | ) : ( 29 | 30 | ); 31 | }; 32 | 33 | export default RequireUser; 34 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | .spinner-container { 2 | position: relative; 3 | width: 160px; 4 | height: 160px; 5 | } 6 | 7 | .main-wand { 8 | position: absolute; 9 | left: calc(50% - 60px); 10 | top: calc(50% - 60px); 11 | } 12 | 13 | .spinner { 14 | border: 8px solid #f3f3f3; /* Light grey */ 15 | border-top: 8px solid #5db9d1; /* Blue */ 16 | border-radius: 50%; 17 | width: 120px; 18 | height: 120px; 19 | animation: spin 2s linear infinite; 20 | position: absolute; 21 | left: calc(50% - 60px); 22 | top: calc(50% - 60px); 23 | } 24 | 25 | .stars1 { 26 | fill: #dd7f36; 27 | animation: flicker 1s linear infinite; 28 | } 29 | 30 | .stars2 { 31 | fill: #dd7f36; 32 | animation: flicker 2s linear infinite; 33 | } 34 | 35 | .stars3 { 36 | fill: #dd7f36; 37 | animation: flicker 3s linear infinite; 38 | } 39 | 40 | .stars4 { 41 | fill: #dd7f36; 42 | animation: flicker 4s linear infinite; 43 | } 44 | 45 | .wandfill { 46 | fill: #7fcde0; 47 | } 48 | 49 | .wand { 50 | fill: #5ebad2; 51 | } 52 | 53 | @keyframes spin { 54 | 0% { 55 | transform: rotate(0deg); 56 | } 57 | 100% { 58 | transform: rotate(360deg); 59 | } 60 | } 61 | 62 | @keyframes flicker { 63 | 0%, 64 | 30%, 65 | 22%, 66 | 62%, 67 | 64%, 68 | 64.999%, 69 | 70%, 70 | 100% { 71 | opacity: 0.99; 72 | } 73 | 20%, 74 | 22%, 75 | 63%, 76 | 63.999%, 77 | 65%, 78 | 69.999% { 79 | opacity: 0.5; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import { BrowserRouter as Router } from "react-router-dom"; 4 | import { Provider } from "react-redux"; 5 | 6 | import "./index.css"; 7 | 8 | import App from "./App"; 9 | import reportWebVitals from "./reportWebVitals"; 10 | import { store } from "./redux/store"; 11 | 12 | const root = ReactDOM.createRoot( 13 | document.getElementById("root") as HTMLElement 14 | ); 15 | root.render( 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | ); 24 | 25 | // If you want to start measuring performance in your app, pass a function 26 | // to log results (for example: reportWebVitals(console.log)) 27 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 28 | reportWebVitals(); 29 | -------------------------------------------------------------------------------- /src/pages/admin/blogcreate.page.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { 3 | Container, 4 | Box, 5 | Typography, 6 | Divider, 7 | Autocomplete, 8 | TextField, 9 | } from "@mui/material"; 10 | 11 | const ContentGenerator = () => { 12 | const [options] = useState([]); 13 | 14 | return ( 15 | <> 16 | 22 | 34 | Search Content 35 | 36 | ( 40 | 45 | )} 46 | /> 47 | 48 | 49 | 50 | 51 | ); 52 | }; 53 | 54 | export default ContentGenerator; 55 | -------------------------------------------------------------------------------- /src/pages/admin/dashboard.page.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Container, 3 | Stack, 4 | Typography, 5 | Box, 6 | TextField, 7 | MenuItem, 8 | SvgIcon, 9 | InputAdornment, 10 | } from "@mui/material"; 11 | 12 | import { ReactComponent as CalendarIcon } from "../../assets/ico_calendar.svg"; 13 | 14 | const Dashboard = () => { 15 | return ( 16 | <> 17 | 18 | 19 | 20 | Your Overview 21 | 22 | 23 | 24 | Last 7 days 25 | 26 | 31 | 32 | 33 | 34 | 35 | ), 36 | }} 37 | defaultValue="15 Mar. - 30 Mar." 38 | /> 39 | 40 | 41 | Compared to 42 | 43 | Previous period 44 | 45 | 46 | Daily 47 | 48 | 49 | Product 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | TOTAl USERS 59 | 60 | 65 | +20% Increase 66 | 67 | 68 | 69 | 200 70 | 71 | 72 | 78 | More Info > 79 | 80 | 81 | 82 | 83 | 84 | 85 | TOTAL REVENUE 86 | 87 | 92 | +20% Increase 93 | 94 | 95 | 96 | $12,989 97 | 98 | 99 | 105 | More Info > 106 | 107 | 108 | 109 | 110 | 111 | 112 | TOTAL WISHES 113 | 114 | 119 | +20 New Wisher 120 | 121 | 122 | 123 | 78 124 | 125 | 126 | 132 | More Info > 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | ); 141 | }; 142 | 143 | export default Dashboard; 144 | -------------------------------------------------------------------------------- /src/pages/admin/price.page.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { 3 | Box, 4 | Typography, 5 | Divider, 6 | Container, 7 | Stack, 8 | Grid, 9 | TextField, 10 | SvgIcon, 11 | IconButton, 12 | MenuItem, 13 | Autocomplete, 14 | } from "@mui/material"; 15 | import { LoadingButton } from "@mui/lab"; 16 | import { object, string, TypeOf, array, number } from "zod"; 17 | import { SubmitHandler, useForm, FormProvider } from "react-hook-form"; 18 | import { zodResolver } from "@hookform/resolvers/zod"; 19 | import { toast } from "react-toastify"; 20 | 21 | import { ReactComponent as DeleteIcon } from "../../assets/ico_del.svg"; 22 | import { ReactComponent as PlustIcon } from "../../assets/ico_plus.svg"; 23 | import { IPlanDetail, IProductHeadings } from "../../redux/api/types"; 24 | import { 25 | useLazyGetProductQuery, 26 | useLazySearchProductQuery, 27 | useUpdatePriceMutation, 28 | } from "../../redux/api/productApi"; 29 | 30 | const saveSchema = object({ 31 | product_name: string().min(1, "Product name is required"), 32 | product_module: string().optional(), 33 | module_description: string().optional(), 34 | plan_details: array( 35 | object({ 36 | plan_name: string(), 37 | total_wishes: number().optional(), 38 | price: string(), 39 | period: string(), 40 | }) 41 | ), 42 | }); 43 | 44 | export type PriceSettingSaveInput = TypeOf; 45 | 46 | const PriceConfigurator = () => { 47 | const [options, setOptions] = useState([]); 48 | const [filter, setFilter] = useState(null); 49 | const [plans, setPlans] = useState([ 50 | { 51 | plan_name: "", 52 | total_wishes: 0, 53 | price: "", 54 | period: "", 55 | }, 56 | ]); 57 | 58 | const methods = useForm({ 59 | resolver: zodResolver(saveSchema), 60 | defaultValues: {}, 61 | }); 62 | 63 | const [searchProduct, searchState] = useLazySearchProductQuery(); 64 | const [getProduct, getState] = useLazyGetProductQuery(); 65 | const [updatePrice, updateState] = useUpdatePriceMutation(); 66 | 67 | const { handleSubmit, register, setValue } = methods; 68 | 69 | useEffect(() => { 70 | searchProduct(""); 71 | // eslint-disable-next-line react-hooks/exhaustive-deps 72 | }, []); 73 | 74 | useEffect(() => { 75 | if (searchState.data) setOptions(searchState.data); 76 | }, [searchState]); 77 | 78 | useEffect(() => { 79 | if (updateState.isSuccess) 80 | toast.success("Price plan updated successfully!"); 81 | }, [updateState]); 82 | 83 | useEffect(() => { 84 | const { data } = getState; 85 | setValue("product_name", data?.product_name as string); 86 | setValue("product_module", data?.product_module as string); 87 | setValue("module_description", data?.module_description as string); 88 | if (data?.plan_details) setPlans([...data.plan_details]); 89 | else 90 | setPlans([ 91 | { 92 | plan_name: "", 93 | total_wishes: 0, 94 | price: "", 95 | period: "", 96 | }, 97 | ]); 98 | }, [getState, setValue]); 99 | 100 | const onSubmitHandler: SubmitHandler = ( 101 | values: PriceSettingSaveInput 102 | ) => { 103 | console.log(values); 104 | updatePrice(values); 105 | }; 106 | 107 | return ( 108 | <> 109 | 110 | 122 | Pricing Configurator 123 | 124 | 125 | 126 | 127 | 128 | 129 | ( 134 | 140 | )} 141 | getOptionLabel={(option) => 142 | `${option.product_name} : ${option.product_module}` 143 | } 144 | renderOption={(props, option) => { 145 | return ( 146 |
  • 147 | {option.product_name} : {option.product_module} 148 |
  • 149 | ); 150 | }} 151 | value={filter} 152 | onChange={(event, newValue) => { 153 | if (newValue) { 154 | setFilter(newValue); 155 | getProduct(newValue); 156 | } 157 | }} 158 | /> 159 |
    160 | 168 | 169 | 170 | 176 | Configure pricing here 177 | 178 | 179 | 185 | Save 186 | 187 | 188 | 189 | 190 | 191 | 192 | 197 | Product Name 198 | 199 | 200 | 201 | 209 | 210 | 211 | 212 | 213 | 218 | Product Module 219 | 220 | 221 | 228 | 234 | 239 | 240 | 241 | 242 | 243 | 248 | Plan Details 249 | 250 | 251 | 258 | 259 | Plan Name 260 | Total Wishes 261 | Price 262 | Period 263 | 264 | 265 | {plans.map((plan, index) => ( 266 | 271 | { 277 | plans[index].plan_name = e.target.value; 278 | setPlans([...plans]); 279 | }} 280 | /> 281 | 291 | ) => { 292 | plans[index].total_wishes = e.target.valueAsNumber; 293 | setPlans([...plans]); 294 | }} 295 | /> 296 | { 302 | plans[index].price = e.target.value; 303 | setPlans([...plans]); 304 | }} 305 | /> 306 | { 312 | plans[index].period = e.target.value; 313 | setPlans([...plans]); 314 | }} 315 | > 316 | Monthly 317 | Yearly 318 | 319 | 324 | { 327 | plans.splice(index, 1); 328 | if (plans.length === 0) 329 | setPlans([ 330 | { 331 | plan_name: "", 332 | total_wishes: 0, 333 | price: "", 334 | period: "", 335 | }, 336 | ]); 337 | else setPlans([...plans]); 338 | }} 339 | > 340 | 341 | 342 | 343 | 344 | {index === plans.length - 1 && ( 345 | 348 | setPlans([ 349 | ...plans, 350 | { 351 | plan_name: "", 352 | total_wishes: 0, 353 | price: "", 354 | period: "", 355 | }, 356 | ]) 357 | } 358 | > 359 | 360 | 361 | 362 | 363 | )} 364 | 365 | 366 | ))} 367 | 368 | 369 | 370 | 371 | 372 |
    373 |
    374 | 375 | ); 376 | }; 377 | 378 | export default PriceConfigurator; 379 | -------------------------------------------------------------------------------- /src/pages/admin/prompt.page.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { 3 | Box, 4 | Typography, 5 | Divider, 6 | Container, 7 | Stack, 8 | Button, 9 | SvgIcon, 10 | } from "@mui/material"; 11 | 12 | import { ReactComponent as PlustWhiteIcon } from "../../assets/ico_plus_white.svg"; 13 | import Prompt from "../../components/Prompt"; 14 | import { IPromptAcceptSchema } from "../../redux/api/types"; 15 | import { useGetProductsNamesQuery } from "../../redux/api/productApi"; 16 | import { useGetPromptsQuery } from "../../redux/api/promptApi"; 17 | 18 | const PromptConfigurator = () => { 19 | const [prompts, setPrompts] = useState([]); 20 | const [products, setProducts] = useState([]); 21 | 22 | const getNamesState = useGetProductsNamesQuery(); 23 | const getPromptsState = useGetPromptsQuery(); 24 | 25 | useEffect(() => { 26 | if (getNamesState.isSuccess) setProducts(getNamesState.data); 27 | }, [getNamesState]); 28 | 29 | useEffect(() => { 30 | if (getPromptsState.isSuccess) setPrompts([...getPromptsState.data]); 31 | }, [getPromptsState]); 32 | 33 | return ( 34 | <> 35 | 36 | 48 | Prompt Configurator 49 | 50 | 51 | 52 | 53 | 54 | 55 | 80 | 81 | {prompts.map((prompt, index) => ( 82 | 93 | ))} 94 | 95 | 96 | 97 | ); 98 | }; 99 | 100 | export default PromptConfigurator; 101 | -------------------------------------------------------------------------------- /src/pages/auth/forgot.page.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Container, 3 | Grid, 4 | Stack, 5 | Typography, 6 | FormControl, 7 | TextField, 8 | Box, 9 | SvgIcon, 10 | } from "@mui/material"; 11 | import { KeyboardBackspace } from "@mui/icons-material"; 12 | import { Link, useNavigate } from "react-router-dom"; 13 | import { toast } from "react-toastify"; 14 | 15 | import BackSignin from "../../assets/back_signin.png"; 16 | import Logo from "../../logo.svg"; 17 | import { useEffect, useState } from "react"; 18 | import { useResetPasswordMutation } from "../../redux/api/authApi"; 19 | import { LoadingButton } from "@mui/lab"; 20 | 21 | const ForgotPage = () => { 22 | const [email, setEmail] = useState(""); 23 | 24 | const [resetPassword, resetState] = useResetPasswordMutation(); 25 | 26 | const navigate = useNavigate(); 27 | 28 | useEffect(() => { 29 | if (resetState.isSuccess) { 30 | toast.success("Password reset!"); 31 | navigate("/signin"); 32 | } 33 | if (resetState.isError) { 34 | if (Array.isArray((resetState.error as any).data.detail)) { 35 | (resetState.error as any).data.detail.map((el: any) => 36 | toast.error(`${el.loc[1]} ${el.msg}`) 37 | ); 38 | } else toast.error((resetState.error as any).data.detail); 39 | } 40 | }, [resetState]); 41 | 42 | return ( 43 | <> 44 | 45 | 46 | 47 | Signup Background 48 | 57 | Audit, Fix, Refactor, Document any Code Within Seconds 58 | 59 | 60 | 68 | 76 | Logo 77 | 78 | CodeGenie 79 | 80 | 81 | 82 | 83 | Forgot password? 84 | 85 | 86 | No worries, we will send you reset instructions 87 | 88 | 89 | 90 | 91 | Email address 92 | 93 | { 100 | setEmail(e.target.value); 101 | }} 102 | /> 103 | 104 | 105 | { 115 | if (email === "") return; 116 | resetPassword({ email }); 117 | }} 118 | loading={resetState.isLoading} 119 | > 120 | Reset Password 121 | 122 | 123 | 132 | 133 | 134 | 135 | Back to log in 136 | 137 | 138 | 139 | 140 | 141 | 142 | ); 143 | }; 144 | 145 | export default ForgotPage; 146 | -------------------------------------------------------------------------------- /src/pages/auth/signin.page.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { 3 | Container, 4 | Grid, 5 | Box, 6 | Typography, 7 | Stack, 8 | Divider, 9 | FormControl, 10 | TextField, 11 | SvgIcon, 12 | } from "@mui/material"; 13 | import { Link, useNavigate } from "react-router-dom"; 14 | import { useForm, FormProvider, SubmitHandler } from "react-hook-form"; 15 | import { object, string, TypeOf } from "zod"; 16 | import { zodResolver } from "@hookform/resolvers/zod"; 17 | import { toast } from "react-toastify"; 18 | import { 19 | LoginSocialGoogle, 20 | LoginSocialGithub, 21 | IResolveParams, 22 | } from "reactjs-social-login"; 23 | import { LoadingButton } from "@mui/lab"; 24 | 25 | import BackSignin from "../../assets/back_signin.png"; 26 | import Logo from "../../logo.svg"; 27 | 28 | import { ReactComponent as GoogleIcon } from "../../assets/ico_google.svg"; 29 | import { ReactComponent as FacebookIcon } from "../../assets/ico_facebook.svg"; 30 | import { ReactComponent as TwitterIcon } from "../../assets/ico_twtter.svg"; 31 | import { ReactComponent as GithubIcon } from "../../assets/ico_github.svg"; 32 | import { ReactComponent as LinkedinIcon } from "../../assets/ico_linkedin.svg"; 33 | 34 | import { 35 | useSigninUserMutation, 36 | useSocialAuthMutation, 37 | } from "../../redux/api/authApi"; 38 | import { useAppDispatch } from "../../redux/store"; 39 | import { setModule } from "../../redux/features/genieSlice"; 40 | 41 | const signinSchema = object({ 42 | email: string() 43 | .min(1, "Email address is required") 44 | .email("Email address is invalid"), 45 | password: string() 46 | .min(1, "Password is required") 47 | .min(8, "Password must be more than 8 characters"), 48 | }); 49 | 50 | export type SigninInput = TypeOf; 51 | 52 | const SigninPage = () => { 53 | const [signinUser, signinState] = useSigninUserMutation(); 54 | const navigate = useNavigate(); 55 | 56 | const dispatch = useAppDispatch(); 57 | const [authSocial, socialState] = useSocialAuthMutation(); 58 | 59 | useEffect(() => { 60 | if (signinState.isSuccess) { 61 | toast.success("Login Success"); 62 | if (!signinState.data.verified) navigate("/verify"); 63 | else { 64 | if (signinState.data.role === "user") { 65 | localStorage.setItem("module", "All Code"); 66 | dispatch(setModule("All Code")); 67 | navigate("/codegenie/all_code"); 68 | } else { 69 | navigate("/admin/dashboard"); 70 | } 71 | } 72 | } 73 | if (signinState.isError) { 74 | console.log(signinState); 75 | if (Array.isArray((signinState.error as any).data.detail)) { 76 | (signinState.error as any).data.detail.map((el: any) => 77 | toast.error(`${el.loc[1]} ${el.msg}`) 78 | ); 79 | } else toast.error((signinState.error as any).data.detail); 80 | } 81 | // eslint-disable-next-line react-hooks/exhaustive-deps 82 | }, [signinState]); 83 | 84 | useEffect(() => { 85 | if (socialState.isSuccess) { 86 | toast.success("Social singup success"); 87 | if (socialState.data.role === "user") { 88 | localStorage.setItem("module", "All Code"); 89 | dispatch(setModule("All Code")); 90 | navigate("/codegenie/all_code"); 91 | } else { 92 | navigate("/admin/dashboard"); 93 | } 94 | } 95 | if (socialState.isError) { 96 | if (Array.isArray((socialState.error as any).data.detail)) { 97 | (socialState.error as any).data.detail.map((el: any) => 98 | toast.error(`${el.loc[1]} ${el.msg}`) 99 | ); 100 | } else toast.error((socialState.error as any).data.detail); 101 | } 102 | // eslint-disable-next-line react-hooks/exhaustive-deps 103 | }, [socialState]); 104 | 105 | const methods = useForm({ 106 | resolver: zodResolver(signinSchema), 107 | }); 108 | 109 | const { 110 | register, 111 | handleSubmit, 112 | formState: { errors }, 113 | } = methods; 114 | 115 | const onSubmitHandler: SubmitHandler = (values) => { 116 | signinUser(values); 117 | }; 118 | 119 | return ( 120 | <> 121 | 122 | 123 | 124 | Signup Background 125 | 134 | Audit, Fix, Refactor, Document any Code Within Seconds 135 | 136 | 137 | 145 | 153 | Logo 154 | 155 | CodeGenie 156 | 157 | 158 | 159 | 160 | Sign in your account 161 | 162 | 163 | 168 | 169 | 170 | 171 | Email address 172 | 173 | 180 | 181 | 182 | 187 | Password 188 | 195 | Forgot Password? 196 | 197 | 198 | 205 | 206 | 207 | 219 | Log in 220 | 221 | 222 | 223 | 230 | 231 | Or 232 | 233 | 234 | 235 | 244 | { 247 | if (data) 248 | authSocial({ 249 | provider: provider as string, 250 | email: data.email as string, 251 | name: data.name as string, 252 | }); 253 | }} 254 | onReject={(err) => { 255 | console.log(err); 256 | }} 257 | > 258 | 259 | 260 | 261 | 262 | 263 | 272 | 273 | 274 | 275 | 276 | 285 | 286 | 287 | 288 | 289 | 298 | console.log(err)} 304 | redirect_uri={window.location.href} 305 | onResolve={({ provider, data }: IResolveParams) => { 306 | if (data) 307 | authSocial({ 308 | provider: provider as string, 309 | username: data.login as string, 310 | name: data.name, 311 | }); 312 | }} 313 | > 314 | 315 | 316 | 317 | 318 | 319 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | New user?{" "} 335 | 341 | Create an account 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | ); 350 | }; 351 | 352 | export default SigninPage; 353 | -------------------------------------------------------------------------------- /src/pages/auth/signup.page.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { 3 | Grid, 4 | Box, 5 | Typography, 6 | Container, 7 | Stack, 8 | SvgIcon, 9 | Divider, 10 | FormControl, 11 | TextField, 12 | Checkbox, 13 | } from "@mui/material"; 14 | import { Link } from "react-router-dom"; 15 | import { 16 | LoginSocialGoogle, 17 | LoginSocialGithub, 18 | IResolveParams, 19 | } from "reactjs-social-login"; 20 | import { toast } from "react-toastify"; 21 | import { object, string, TypeOf } from "zod"; 22 | import { useForm, FormProvider, SubmitHandler } from "react-hook-form"; 23 | import { zodResolver } from "@hookform/resolvers/zod"; 24 | import { useNavigate } from "react-router-dom"; 25 | import { LoadingButton } from "@mui/lab"; 26 | 27 | import { ReactComponent as GoogleIcon } from "../../assets/ico_google.svg"; 28 | import { ReactComponent as FacebookIcon } from "../../assets/ico_facebook.svg"; 29 | import { ReactComponent as TwitterIcon } from "../../assets/ico_twtter.svg"; 30 | import { ReactComponent as GithubIcon } from "../../assets/ico_github.svg"; 31 | import { ReactComponent as LinkedinIcon } from "../../assets/ico_linkedin.svg"; 32 | 33 | import BackSignup from "../../assets/back_signup.png"; 34 | import Logo from "../../assets/logo_white.png"; 35 | 36 | import { 37 | useSignupUserMutation, 38 | useSocialAuthMutation, 39 | } from "../../redux/api/authApi"; 40 | import { useAppDispatch } from "../../redux/store"; 41 | import { setModule } from "../../redux/features/genieSlice"; 42 | 43 | const signupSchema = object({ 44 | name: string().min(1, "Full name is required"), 45 | email: string() 46 | .min(1, "Email address is required") 47 | .email("Email address is invalid"), 48 | password: string() 49 | .min(1, "Password is required") 50 | .min(8, "Password must be more than 8 characters"), 51 | passwordConfirm: string().min(1, "Please confirm your password"), 52 | }).refine((data) => data.password === data.passwordConfirm, { 53 | path: ["passwordConfirm"], 54 | message: "Passwords do not match", 55 | }); 56 | 57 | export type SignupInput = TypeOf; 58 | 59 | const SignupPage = () => { 60 | const [termsCheck, setTermsCheck] = useState(false); 61 | 62 | const [authSocial, socialState] = useSocialAuthMutation(); 63 | const [signupUser, signupState] = useSignupUserMutation(); 64 | 65 | const navigate = useNavigate(); 66 | const dispatch = useAppDispatch(); 67 | 68 | const methods = useForm({ 69 | resolver: zodResolver(signupSchema), 70 | }); 71 | 72 | const { 73 | register, 74 | handleSubmit, 75 | formState: { errors }, 76 | } = methods; 77 | 78 | useEffect(() => { 79 | if (socialState.isSuccess) { 80 | toast.success("Social singup success"); 81 | if (socialState.data.role === "user") { 82 | localStorage.setItem("module", "All Code"); 83 | dispatch(setModule("All Code")); 84 | navigate("/codegenie/all_code"); 85 | } else { 86 | navigate("/admin/dashboard"); 87 | } 88 | } 89 | if (socialState.isError) { 90 | if (Array.isArray((socialState.error as any).data.detail)) { 91 | (socialState.error as any).data.detail.map((el: any) => 92 | toast.error(`${el.loc[1]} ${el.msg}`) 93 | ); 94 | } else toast.error((socialState.error as any).data.detail); 95 | } 96 | // eslint-disable-next-line react-hooks/exhaustive-deps 97 | }, [socialState]); 98 | 99 | useEffect(() => { 100 | if (signupState.isSuccess) { 101 | toast.success("User created successfully"); 102 | navigate("/signin"); 103 | } 104 | if (signupState.isError) { 105 | if (Array.isArray((signupState.error as any).data.detail)) { 106 | (signupState.error as any).data.detail.map((el: any) => 107 | toast.error(`${el.loc[1]} ${el.msg}`) 108 | ); 109 | } else toast.error((signupState.error as any).data.detail); 110 | } 111 | // eslint-disable-next-line react-hooks/exhaustive-deps 112 | }, [signupState]); 113 | 114 | const onSubmitHandler: SubmitHandler = (values) => { 115 | signupUser(values); 116 | }; 117 | 118 | return ( 119 | <> 120 | 121 | 122 | 123 | Signup Background 124 | 132 | Logo 133 | 134 | CodeGenie 135 | 136 | 137 | 146 | Audit, Fix, Refactor, Document any Code Within Seconds 147 | 148 | 149 | 156 | 157 | 158 | Create an account 159 | 160 | 161 | Sign up with social 162 | 163 | 164 | 173 | { 176 | if (data) 177 | authSocial({ 178 | provider: provider as string, 179 | email: data.email as string, 180 | name: data.name as string, 181 | }); 182 | }} 183 | onReject={(err) => { 184 | console.log(err); 185 | }} 186 | > 187 | 188 | 189 | 190 | 191 | 192 | 201 | 202 | 203 | 204 | 205 | 214 | 215 | 216 | 217 | 218 | 227 | console.log(err)} 233 | redirect_uri={window.location.href} 234 | onResolve={({ provider, data }: IResolveParams) => { 235 | if (data) 236 | authSocial({ 237 | provider: provider as string, 238 | username: data.login as string, 239 | name: data.name, 240 | }); 241 | }} 242 | > 243 | 244 | 245 | 246 | 247 | 248 | 257 | 258 | 259 | 260 | 261 | 262 | 269 | 270 | Or 271 | 272 | 273 | 274 | 279 | 280 | 281 | 282 | Name 283 | 284 | 290 | 291 | 292 | 293 | Email address 294 | 295 | 302 | 303 | 304 | 305 | Password 306 | 307 | 314 | 315 | 316 | 317 | Confirm Password 318 | 319 | 326 | 327 | 328 | setTermsCheck(e.target.checked)} 331 | />{" "} 332 | 333 | I agree to the{" "} 334 | 340 | Terms & Privacy Policy 341 | 342 | 343 | 344 | 345 | 358 | Sign Up 359 | 360 | 361 | 362 | 363 | Have an account?{" "} 364 | 370 | Sign In 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | ); 379 | }; 380 | 381 | export default SignupPage; 382 | -------------------------------------------------------------------------------- /src/pages/auth/terms.page.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Typography, Container, Divider, SvgIcon } from "@mui/material"; 2 | import { Link } from "react-router-dom"; 3 | import { KeyboardBackspace } from "@mui/icons-material"; 4 | 5 | const TermsPage = () => { 6 | return ( 7 | <> 8 | 9 | 10 | 17 | Code Genie Terms of Service 18 | 19 | 20 | Last Updated: August 3, 2023 21 |
    22 |
    23 | These Terms of Service ("Terms") govern your access to and use of 24 | Code Genie, a product owned and operated by OceSha, Inc., a Florida 25 | company ("we," "our," or "us"). Please read these Terms carefully 26 | before using Code Genie. 27 |
    28 | 29 | 1. Acceptance of Terms 30 | 31 | 32 | By accessing or using Code Genie, you agree to be bound by these 33 | Terms. If you do not agree with these Terms, you must not access or 34 | use Code Genie. 35 | 36 | 37 | 2. Registration 38 | 39 | 40 | You may be required to register for an account to use certain 41 | features of Code Genie. You agree to provide accurate and complete 42 | information during registration and to keep your account information 43 | up to date. 44 | 45 | 46 | 3. Use of Code Genie 47 | 48 | 49 | You agree to use Code Genie in accordance with all applicable laws 50 | and regulations. You may not: 51 | 52 | 53 | Use Code Genie for any unlawful or fraudulent purpose 54 | 55 | 56 | Interfere with or disrupt the operation of Code Genie 57 | 58 | 59 | Reverse engineer, decompile, or disassemble Code Genie 60 | 61 | 62 | Use Code Genie in a way that infringes the rights of others 63 | 64 | 65 | 66 | 67 | 68 | 4. Intellectual Property 69 | 70 | 71 | Code Genie and its content, features, and functionality are owned by 72 | OceSha, Inc. and are protected by intellectual property laws. You 73 | are granted a limited, non-exclusive, non-transferable license to 74 | use Code Genie for your personal or business use. You may not 75 | reproduce, distribute, or create derivative works of Code Genie 76 | without our express written permission. 77 | 78 | 79 | 5. User Content 80 | 81 | 82 | You may be able to submit content to Code Genie, such as code, 83 | comments, and feedback. You retain ownership of your content, but 84 | you grant us a worldwide, royalty-free license to use, reproduce, 85 | modify, and distribute your content as necessary to provide Code 86 | Genie. 87 | 88 | 89 | 6. Termination 90 | 91 | 92 | We may terminate or suspend your access to Code Genie at our sole 93 | discretion, without notice, for conduct that we believe violates 94 | these Terms or is harmful to us, other users of Code Genie, or third 95 | parties, or for any other reason. 96 | 97 | 98 | 7. Disclaimers 99 | 100 | 101 | Code Genie is provided "as is" and "as available" without any 102 | warranties of any kind, either express or implied. We do not warrant 103 | that Code Genie will be uninterrupted, error-free, or free of 104 | harmful components. 105 | 106 | 107 | 8. Limitation of Liability 108 | 109 | 110 | To the fullest extent permitted by law, OceSha, Inc. shall not be 111 | liable for any indirect, incidental, special, consequential, or 112 | punitive damages, or any loss of profits or revenues, whether 113 | incurred directly or indirectly, arising out of or in connection 114 | with your use of Code Genie. 115 | 116 | 117 | 9. Governing Law 118 | 119 | 120 | These Terms are governed by the laws of the State of Delaware, USA, 121 | without regard to its conflict of law principles. 122 | 123 | 124 | 10. Changes to Terms 125 | 126 | 127 | We may revise these Terms from time to time. We will notify you of 128 | any changes by posting the updated Terms on this page. Your 129 | continued use of Code Genie after the changes are posted constitutes 130 | your acceptance of the new Terms. 131 | 132 | 133 | 11. Contact Us 134 | 135 | 136 | If you have any questions or concerns about these Terms, please 137 | contact us at: 138 |
    139 |
    140 | OceSha, Inc. 141 |
    142 | Los Angeles, Ca 143 |
    144 | 310-748-8901 145 |
    146 | support@ocesha.com 147 |
    148 | 149 | 150 | By using Code Genie, you acknowledge that you have read, understood, 151 | and agree to be bound by these Terms. If you do not agree with these 152 | Terms, please do not use Code Genie. 153 | 154 | 163 | 164 | 165 | 166 | Back to log in 167 | 168 |
    169 |
    170 | 171 | ); 172 | }; 173 | 174 | export default TermsPage; 175 | -------------------------------------------------------------------------------- /src/pages/auth/verify.page.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { LoadingButton } from "@mui/lab"; 3 | import { 4 | Container, 5 | Stack, 6 | Card, 7 | Typography, 8 | TextField, 9 | Box, 10 | } from "@mui/material"; 11 | import { toast } from "react-toastify"; 12 | import { object, string, TypeOf } from "zod"; 13 | import { useForm, FormProvider, SubmitHandler } from "react-hook-form"; 14 | import { zodResolver } from "@hookform/resolvers/zod"; 15 | import { useNavigate } from "react-router-dom"; 16 | 17 | import { useVerifyEmailMutation } from "../../redux/api/authApi"; 18 | import { useAppDispatch } from "../../redux/store"; 19 | import { setModule } from "../../redux/features/genieSlice"; 20 | 21 | const verifySchema = object({ 22 | code: string().min(1, "Verification code is required"), 23 | }); 24 | 25 | export type VerifyInput = TypeOf; 26 | 27 | const VerifyPage = () => { 28 | const [verifyEmail, verifyState] = useVerifyEmailMutation(); 29 | const dispatch = useAppDispatch(); 30 | const navigate = useNavigate(); 31 | 32 | const methods = useForm({ 33 | resolver: zodResolver(verifySchema), 34 | }); 35 | 36 | const { 37 | register, 38 | handleSubmit, 39 | formState: { errors }, 40 | } = methods; 41 | 42 | const onSubmitHandler: SubmitHandler = (values) => { 43 | verifyEmail(values); 44 | }; 45 | 46 | useEffect(() => { 47 | if (verifyState.isSuccess) { 48 | toast.success("Verified successfully"); 49 | localStorage.setItem("module", "All Code"); 50 | dispatch(setModule("All Code")); 51 | navigate("/codegenie/all_code"); 52 | } 53 | if (verifyState.isError) { 54 | if (Array.isArray((verifyState.error as any).data.detail)) { 55 | (verifyState.error as any).data.detail.map((el: any) => 56 | toast.error(`${el.loc[1]} ${el.msg}`) 57 | ); 58 | } else toast.error((verifyState.error as any).data.detail); 59 | } 60 | // eslint-disable-next-line react-hooks/exhaustive-deps 61 | }, [verifyState]); 62 | 63 | return ( 64 | <> 65 | 66 | 67 | 68 | 73 | 74 | 75 | Verification Code 76 | 77 | 85 | 92 | Send 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | ); 101 | }; 102 | 103 | export default VerifyPage; 104 | -------------------------------------------------------------------------------- /src/pages/genie.page.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState, useRef } from "react"; 2 | import { 3 | Container, 4 | Link, 5 | Typography, 6 | Breadcrumbs, 7 | Divider, 8 | Grid, 9 | Stack, 10 | SvgIcon, 11 | Box, 12 | Tabs, 13 | Tab, 14 | Backdrop, 15 | CircularProgress, 16 | Modal, 17 | Button, 18 | TextField, 19 | } from "@mui/material"; 20 | import { NavigateNext } from "@mui/icons-material"; 21 | import { styled } from "@mui/material/styles"; 22 | import CodeEditor from "@uiw/react-textarea-code-editor"; 23 | import { LoadingButton } from "@mui/lab"; 24 | import { toast } from "react-toastify"; 25 | import { 26 | useSendUrlMutation, 27 | useUploadImageMutation, 28 | } from "../redux/api/genieApi"; 29 | 30 | import { ReactComponent as TextIcon } from "../assets/ico_text.svg"; 31 | import { ReactComponent as ImageIcon } from "../assets/ico_image.svg"; 32 | import { ReactComponent as UrlIcon } from "../assets/ico_url.svg"; 33 | import { ReactComponent as WishIcon } from "../assets/ico_wish.svg"; 34 | import TotalPanel from "../components/genie/TotalPanel"; 35 | import { useAppDispatch, useAppSelector } from "../redux/store"; 36 | import { useGetProductQuery } from "../redux/api/productApi"; 37 | import { useRunPromptMutation } from "../redux/api/promptApi"; 38 | import { setMsg } from "../redux/features/genieSlice"; 39 | import { useNavigate } from "react-router-dom"; 40 | import MagicWand from "../components/MagicWand"; 41 | 42 | const loadTypes = [ 43 | { 44 | icon: ( 45 | 53 | 54 | 55 | 56 | 57 | ), 58 | }, 59 | { 60 | icon: ( 61 | 69 | 70 | 71 | 72 | 73 | ), 74 | }, 75 | { 76 | icon: ( 77 | 85 | 86 | 87 | 88 | 89 | ), 90 | }, 91 | ]; 92 | 93 | const CodeBox = styled(Box)(({ theme }) => ({ 94 | border: "1px solid", 95 | borderColor: theme.palette.mode === "dark" ? "#737373" : "#E3E3E3", 96 | borderRadius: 4, 97 | height: "500px", 98 | overflow: "auto", 99 | "::-webkit-scrollbar": { 100 | width: 4, 101 | }, 102 | "::-webkit-scrollbar-thumb": { 103 | backgroundColor: theme.palette.divider, 104 | borderRadius: 4, 105 | }, 106 | "::-webkit-scrollbar-track": { 107 | backgroundColor: theme.palette.mode === "dark" ? "#2D2D2D" : "#fff", 108 | }, 109 | })); 110 | 111 | const GeniePage = () => { 112 | const [value, setValue] = useState(0); 113 | const [code, setCode] = useState(``); 114 | const [open, setOpen] = useState(false); 115 | const wrapperRef = useRef(null); 116 | const [url, setURL] = useState(""); 117 | 118 | const genieSelector = useAppSelector((state) => state.genieState); 119 | const dipatch = useAppDispatch(); 120 | const navigate = useNavigate(); 121 | 122 | const productState = useGetProductQuery({ 123 | _id: "", 124 | product_name: "CodeGenie", 125 | product_module: genieSelector.module ? genieSelector.module : "", 126 | }); 127 | 128 | const [runPrompt, runState] = useRunPromptMutation(); 129 | const [uploadImage, uploadState] = useUploadImageMutation(); 130 | const [sendUrl, urlState] = useSendUrlMutation(); 131 | 132 | useEffect(() => { 133 | if (uploadState.isSuccess) { 134 | setCode(uploadState.data.content); 135 | } 136 | }, [uploadState]); 137 | 138 | useEffect(() => { 139 | if (runState.isSuccess) { 140 | toast.success("Executed successfully"); 141 | dipatch(setMsg(runState.data)); 142 | } 143 | // eslint-disable-next-line react-hooks/exhaustive-deps 144 | }, [runState]); 145 | 146 | useEffect(() => { 147 | if (urlState.isSuccess) setCode(urlState.data.content); 148 | }, [urlState]); 149 | 150 | if (productState.isLoading || productState.isFetching || !productState.data) 151 | return ( 152 | theme.zIndex.drawer + 1 }} 154 | open={true} 155 | > 156 | 157 | 158 | ); 159 | 160 | const breadcrumbs = [ 161 | { 166 | navigate("/"); 167 | }} 168 | > 169 | Home 170 | , 171 | 172 | {genieSelector.module} 173 | , 174 | ]; 175 | 176 | const { 177 | module_description, 178 | source_check, 179 | source_text, 180 | source_image, 181 | source_url, 182 | input_box_title, 183 | input_box_description, 184 | export_check, 185 | export_text, 186 | export_pdf, 187 | export_word, 188 | } = productState.data; 189 | 190 | return ( 191 | <> 192 | {runState.isLoading && ( 193 | 194 | theme.zIndex.drawer + 1 }} 196 | open={true} 197 | > 198 | 199 | 200 | Executing your wish 201 | 202 | 203 | 204 | )} 205 | 206 | 207 | } 209 | sx={{ marginY: 3 }} 210 | > 211 | {breadcrumbs} 212 | 213 | 214 | 215 | 216 | {module_description} 217 | 218 | { 223 | let formData = new FormData(); 224 | if (event.target.files) { 225 | formData.append("image", event.target.files[0]); 226 | uploadImage(formData); 227 | } 228 | }} 229 | /> 230 | 232 | setValue(newValue) 233 | } 234 | value={value} 235 | sx={{ 236 | "& .MuiTabs-scroller .MuiTabs-flexContainer": { gap: 2 }, 237 | }} 238 | > 239 | {source_check.includes("source_text") && ( 240 | 253 | )} 254 | {source_check.includes("source_image") && ( 255 | wrapperRef.current?.click()} 268 | /> 269 | )} 270 | {source_check.includes("source_url") && ( 271 | setOpen(true)} 284 | /> 285 | )} 286 | 287 | 288 | {input_box_title} 289 | 290 | 291 | 292 | setCode(evn.target.value)} 297 | padding={15} 298 | style={{ 299 | fontSize: 16, 300 | backgroundColor: "inherit", 301 | fontFamily: 302 | "ui-monospace,SFMono-Regular,SF Mono,Consolas,Liberation Mono,Menlo,monospace", 303 | }} 304 | /> 305 | 306 | 307 | 310 | 311 | 312 | } 313 | loading={runState.isLoading} 314 | sx={{ 315 | background: 316 | "linear-gradient(270deg, #4BA5EB 0%, #0168B5 100%)", 317 | marginLeft: 5, 318 | color: "white", 319 | paddingX: "20px", 320 | paddingY: "12px", 321 | borderRadius: "4px", 322 | width: "fit-content", 323 | }} 324 | onClick={() => { 325 | if (code === "") { 326 | toast.error("No empty code!"); 327 | return; 328 | } 329 | runPrompt({ 330 | product_name: "CodeGenie", 331 | product_module: genieSelector.module 332 | ? genieSelector.module 333 | : "", 334 | code: code, 335 | }); 336 | }} 337 | > 338 | Execute Wish 339 | 340 | 341 | 342 | 343 | 344 | 345 | 351 | 352 | 353 | 354 | 355 | setOpen(false)} 358 | aria-labelledby="child-modal-title" 359 | aria-describedby="child-modal-description" 360 | > 361 | 376 |

    URL

    377 |

    Input code location URL

    378 | setURL(e.target.value)} 381 | error={url === ""} 382 | helperText={url === "" ? "URL field is required." : ""} 383 | size="small" 384 | fullWidth 385 | placeholder="URL" 386 | sx={{ mb: 2 }} 387 | /> 388 | 389 | 399 | 407 | 408 |
    409 |
    410 | 411 | ); 412 | }; 413 | 414 | export default GeniePage; 415 | -------------------------------------------------------------------------------- /src/pages/pricing.page.tsx: -------------------------------------------------------------------------------- 1 | const PricingPage = () => { 2 | return <>; 3 | }; 4 | 5 | export default PricingPage; 6 | -------------------------------------------------------------------------------- /src/pages/unauthorized.page.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Container, Typography } from "@mui/material"; 2 | 3 | const UnauthorizePage = () => { 4 | return ( 5 | 6 | 16 | 21 | Unauthorized Page 22 | 23 | 24 | 25 | ); 26 | }; 27 | 28 | export default UnauthorizePage; 29 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/redux/api/authApi.ts: -------------------------------------------------------------------------------- 1 | import { createApi } from "@reduxjs/toolkit/query/react"; 2 | import customFetchBase from "./customFetchBase"; 3 | import { 4 | IGenericResponse, 5 | ISigninReseponseSchema, 6 | ISocialSignupSchema, 7 | } from "./types"; 8 | import { SignupInput } from "../../pages/auth/signup.page"; 9 | import { SigninInput } from "../../pages/auth/signin.page"; 10 | import { userApi } from "./userApi"; 11 | 12 | export const authApi = createApi({ 13 | reducerPath: "authApi", 14 | baseQuery: customFetchBase, 15 | endpoints: (builder) => ({ 16 | socialAuth: builder.mutation< 17 | { access_token: string; role: string }, 18 | ISocialSignupSchema 19 | >({ 20 | query(data) { 21 | return { 22 | url: "auth/social", 23 | method: "POST", 24 | body: data, 25 | credentials: "include", 26 | }; 27 | }, 28 | async onQueryStarted(args, { dispatch, queryFulfilled }) { 29 | try { 30 | await queryFulfilled; 31 | await dispatch(userApi.endpoints.getMe.initiate(null)); 32 | } catch (error) {} 33 | }, 34 | }), 35 | signupUser: builder.mutation({ 36 | query(data) { 37 | return { 38 | url: "auth/signup", 39 | method: "POST", 40 | body: data, 41 | }; 42 | }, 43 | }), 44 | signinUser: builder.mutation({ 45 | query(data) { 46 | return { 47 | url: "auth/signin", 48 | method: "POST", 49 | body: data, 50 | credentials: "include", 51 | }; 52 | }, 53 | async onQueryStarted(args, { dispatch, queryFulfilled }) { 54 | try { 55 | await queryFulfilled; 56 | await dispatch(userApi.endpoints.getMe.initiate(null)); 57 | } catch (error) {} 58 | }, 59 | }), 60 | verifyEmail: builder.mutation({ 61 | query(data) { 62 | return { 63 | url: "auth/verify", 64 | method: "PATCH", 65 | body: data, 66 | }; 67 | }, 68 | }), 69 | resetPassword: builder.mutation({ 70 | query(data) { 71 | return { 72 | url: "auth/forgot", 73 | method: "PATCH", 74 | body: data, 75 | }; 76 | }, 77 | }), 78 | }), 79 | }); 80 | 81 | export const { 82 | useSocialAuthMutation, 83 | useSignupUserMutation, 84 | useSigninUserMutation, 85 | useVerifyEmailMutation, 86 | useResetPasswordMutation, 87 | } = authApi; 88 | -------------------------------------------------------------------------------- /src/redux/api/customFetchBase.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BaseQueryFn, 3 | FetchArgs, 4 | fetchBaseQuery, 5 | FetchBaseQueryError, 6 | } from "@reduxjs/toolkit/query"; 7 | import { Mutex } from "async-mutex"; 8 | import { logout } from "../features/userSlice"; 9 | 10 | const baseUrl = `${process.env.REACT_APP_SERVER_ENDPOINT}/api/`; 11 | 12 | // Create a new mutex 13 | const mutex = new Mutex(); 14 | 15 | const baseQuery = fetchBaseQuery({ 16 | baseUrl, 17 | }); 18 | 19 | const customFetchBase: BaseQueryFn< 20 | string | FetchArgs, 21 | unknown, 22 | FetchBaseQueryError 23 | > = async (args, api, extraOptions) => { 24 | // wait until the mutex is available without locking it 25 | await mutex.waitForUnlock(); 26 | let result = await baseQuery(args, api, extraOptions); 27 | if ((result.error?.data as any)?.message === "You are not logged in") { 28 | if (!mutex.isLocked()) { 29 | const release = await mutex.acquire(); 30 | 31 | try { 32 | const refreshResult = await baseQuery( 33 | { credentials: "include", url: "auth/refresh" }, 34 | api, 35 | extraOptions 36 | ); 37 | 38 | if (refreshResult.data) { 39 | // Retry the initial query 40 | result = await baseQuery(args, api, extraOptions); 41 | } else { 42 | api.dispatch(logout()); 43 | window.location.href = "/login"; 44 | } 45 | } finally { 46 | // release must be called once the mutex should be released again. 47 | release(); 48 | } 49 | } else { 50 | // wait until the mutex is available without locking it 51 | await mutex.waitForUnlock(); 52 | result = await baseQuery(args, api, extraOptions); 53 | } 54 | } 55 | 56 | return result; 57 | }; 58 | 59 | export default customFetchBase; 60 | -------------------------------------------------------------------------------- /src/redux/api/genieApi.ts: -------------------------------------------------------------------------------- 1 | import { createApi } from "@reduxjs/toolkit/query/react"; 2 | import customFetchBase from "./customFetchBase"; 3 | 4 | export const genieApi = createApi({ 5 | reducerPath: "", 6 | baseQuery: customFetchBase, 7 | endpoints: (builder) => ({ 8 | uploadImage: builder.mutation<{ content: string }, FormData>({ 9 | query(data) { 10 | return { 11 | url: "genie/image", 12 | method: "POST", 13 | credentials: "include", 14 | body: data, 15 | }; 16 | }, 17 | }), 18 | sendUrl: builder.mutation<{ content: string }, { url: string }>({ 19 | query(data) { 20 | return { 21 | url: "genie/url", 22 | method: "POST", 23 | credentials: "include", 24 | body: data, 25 | }; 26 | }, 27 | }), 28 | exportFile: builder.mutation< 29 | { path: string }, 30 | { doc_type: string; advice: any } 31 | >({ 32 | query({ doc_type, advice }) { 33 | return { 34 | url: `genie/export/${doc_type}`, 35 | method: "POST", 36 | credentials: "include", 37 | body: { advice }, 38 | }; 39 | }, 40 | }), 41 | }), 42 | }); 43 | 44 | export const { 45 | useUploadImageMutation, 46 | useSendUrlMutation, 47 | useExportFileMutation, 48 | } = genieApi; 49 | -------------------------------------------------------------------------------- /src/redux/api/productApi.ts: -------------------------------------------------------------------------------- 1 | import { createApi } from "@reduxjs/toolkit/query/react"; 2 | import customFetchBase from "./customFetchBase"; 3 | 4 | import { ProductSettingSaveInput } from "../../pages/admin/product.page"; 5 | import { PriceSettingSaveInput } from "../../pages/admin/price.page"; 6 | import { 7 | IGenericResponse, 8 | IPlanDetail, 9 | IProduct, 10 | IProductHeadings, 11 | } from "./types"; 12 | 13 | export const productApi = createApi({ 14 | reducerPath: "productApi", 15 | baseQuery: customFetchBase, 16 | tagTypes: ["Product"], 17 | endpoints: (builder) => ({ 18 | getProduct: builder.query({ 19 | query({ product_name, product_module }) { 20 | return { 21 | url: `products?product_name=${product_name}&product_module=${product_module}`, 22 | }; 23 | }, 24 | transformResponse: (results: { data: IProduct }) => results.data, 25 | providesTags: [{ type: "Product", id: "LIST" }], 26 | }), 27 | addProduct: builder.mutation({ 28 | query(data) { 29 | return { 30 | url: "products", 31 | method: "POST", 32 | body: data, 33 | }; 34 | }, 35 | invalidatesTags: [{ type: "Product", id: "LIST" }], 36 | }), 37 | updateProduct: builder.mutation({ 38 | query(data) { 39 | return { 40 | url: "products", 41 | method: "PATCH", 42 | body: data, 43 | }; 44 | }, 45 | invalidatesTags: [{ type: "Product", id: "LIST" }], 46 | }), 47 | searchProduct: builder.query({ 48 | query(searchKey: string) { 49 | return { 50 | url: `products/search?search_key=${searchKey}`, 51 | }; 52 | }, 53 | transformResponse: (results: { data: IProductHeadings[] }) => 54 | results.data, 55 | }), 56 | updatePrice: builder.mutation({ 57 | query(data) { 58 | return { 59 | url: "products/update_price", 60 | method: "PATCH", 61 | body: data, 62 | }; 63 | }, 64 | }), 65 | getProductsNames: builder.query({ 66 | query() { 67 | return { 68 | url: "products/names", 69 | method: "GET", 70 | }; 71 | }, 72 | transformResponse: (results: { data: string[] }) => results.data, 73 | }), 74 | getModules: builder.query({ 75 | query(product_name: string) { 76 | return { 77 | url: `products/modules?product_name=${product_name}`, 78 | method: "GET", 79 | }; 80 | }, 81 | transformResponse: (results: { data: { product_module: string }[] }) => 82 | results.data.map((item) => item.product_module), 83 | }), 84 | getPrices: builder.query< 85 | IPlanDetail[] | null, 86 | { product_name: string; product_module: string } 87 | >({ 88 | query({ product_name, product_module }) { 89 | return { 90 | url: `products/prices?product_name=${product_name}&product_module=${product_module}`, 91 | method: "GET", 92 | }; 93 | }, 94 | transformResponse: (results: { 95 | data: { plan_details: IPlanDetail[] } | null; 96 | }) => (results.data ? results.data.plan_details : null), 97 | }), 98 | }), 99 | }); 100 | 101 | export const { 102 | useUpdateProductMutation, 103 | useLazyGetProductQuery, 104 | useGetProductQuery, 105 | useLazySearchProductQuery, 106 | useUpdatePriceMutation, 107 | useAddProductMutation, 108 | useGetProductsNamesQuery, 109 | useLazyGetModulesQuery, 110 | useLazyGetPricesQuery, 111 | } = productApi; 112 | -------------------------------------------------------------------------------- /src/redux/api/promptApi.ts: -------------------------------------------------------------------------------- 1 | import { createApi } from "@reduxjs/toolkit/query/react"; 2 | import customFetchBase from "./customFetchBase"; 3 | 4 | import { 5 | IGenericResponse, 6 | IPromptAcceptSchema, 7 | IPromptRunSchema, 8 | } from "./types"; 9 | import { IPromptSchema } from "../../components/Prompt"; 10 | 11 | export const promptApi = createApi({ 12 | reducerPath: "promptApi", 13 | baseQuery: customFetchBase, 14 | tagTypes: ["Prompt"], 15 | endpoints: (builder) => ({ 16 | addPrompt: builder.mutation({ 17 | query(data) { 18 | return { 19 | url: "prompts", 20 | method: "POST", 21 | body: data, 22 | }; 23 | }, 24 | invalidatesTags: [{ type: "Prompt", id: "LIST" }], 25 | }), 26 | getPrompts: builder.query({ 27 | query() { 28 | return { 29 | url: "prompts", 30 | method: "GET", 31 | }; 32 | }, 33 | transformResponse: (results: { data: IPromptAcceptSchema[] }) => 34 | results.data, 35 | providesTags: [{ type: "Prompt", id: "LIST" }], 36 | }), 37 | updatePrompt: builder.mutation< 38 | IGenericResponse, 39 | { id: string; info: IPromptSchema } 40 | >({ 41 | query({ id, info }) { 42 | return { 43 | url: `prompts/${id}`, 44 | method: "PATCH", 45 | body: info, 46 | }; 47 | }, 48 | invalidatesTags: [{ type: "Prompt", id: "LIST" }], 49 | }), 50 | getPromptNames: builder.query< 51 | string[], 52 | { product_name: string; product_module: string } 53 | >({ 54 | query({ product_name, product_module }) { 55 | return { 56 | url: `prompts/names?product_name=${product_name}&product_module=${product_module}`, 57 | }; 58 | }, 59 | transformResponse: (results: { data: { prompt_name: string }[] }) => 60 | results.data.map((item) => item.prompt_name), 61 | }), 62 | runPrompt: builder.mutation, IPromptRunSchema>({ 63 | query(data) { 64 | return { 65 | url: "prompts/run", 66 | method: "POST", 67 | body: data, 68 | }; 69 | }, 70 | transformResponse: (results: { msg: Array }) => results.msg, 71 | }), 72 | }), 73 | }); 74 | 75 | export const { 76 | useAddPromptMutation, 77 | useGetPromptsQuery, 78 | useUpdatePromptMutation, 79 | useGetPromptNamesQuery, 80 | useRunPromptMutation, 81 | } = promptApi; 82 | -------------------------------------------------------------------------------- /src/redux/api/types.ts: -------------------------------------------------------------------------------- 1 | export interface IGenericResponse { 2 | status: string; 3 | message: string; 4 | } 5 | 6 | export interface IProduct { 7 | id: string; 8 | product_name: string; 9 | product_module: string; 10 | module_description: string; 11 | source_check: string[]; 12 | source_text: string; 13 | source_image: string; 14 | source_url: string; 15 | input_box_title: string; 16 | input_box_description: string; 17 | export_check: string[]; 18 | export_word: string; 19 | export_pdf: string; 20 | export_text: string; 21 | plan_details: IPlanDetail[] | null; 22 | } 23 | 24 | export interface IProductHeadings { 25 | _id: string; 26 | product_name: string; 27 | product_module: string; 28 | } 29 | 30 | export interface IPlanDetail { 31 | plan_name: string; 32 | total_wishes: number; 33 | price: string; 34 | period: string; 35 | } 36 | 37 | export interface IPrompt { 38 | _id: string; 39 | product: string; 40 | plan: string; 41 | module: string; 42 | prompt_name: string; 43 | order: number; 44 | prompt: string; 45 | } 46 | 47 | export interface IPromptAcceptSchema { 48 | _id: string; 49 | product_name: string; 50 | product_module: string; 51 | plan: string; 52 | prompt_name: string; 53 | order: number; 54 | prompt: string; 55 | } 56 | 57 | export interface IPromptRunSchema { 58 | product_name: string; 59 | product_module: string; 60 | code: string; 61 | } 62 | 63 | export interface ISocialSignupSchema { 64 | provider: string; 65 | email?: string | null; 66 | username?: string | null; 67 | name: string; 68 | } 69 | 70 | export interface ISigninReseponseSchema { 71 | access_token: string; 72 | role: string; 73 | verified: boolean; 74 | } 75 | 76 | export interface IUser { 77 | provider: string; 78 | name: string; 79 | email: string | null; 80 | role: string; 81 | username: string | null; 82 | } 83 | -------------------------------------------------------------------------------- /src/redux/api/userApi.ts: -------------------------------------------------------------------------------- 1 | import { createApi } from "@reduxjs/toolkit/query/react"; 2 | import customFetchBase from "./customFetchBase"; 3 | 4 | import { setUser } from "../features/userSlice"; 5 | import { IUser } from "./types"; 6 | 7 | export const userApi = createApi({ 8 | reducerPath: "userApi", 9 | baseQuery: customFetchBase, 10 | tagTypes: ["User"], 11 | endpoints: (builder) => ({ 12 | getMe: builder.query({ 13 | query() { 14 | return { 15 | url: "users/me", 16 | credentials: "include", 17 | }; 18 | }, 19 | transformResponse: (result: { user: IUser }) => result.user, 20 | async onQueryStarted(args, { dispatch, queryFulfilled }) { 21 | try { 22 | const { data } = await queryFulfilled; 23 | dispatch(setUser(data)); 24 | } catch (error) {} 25 | }, 26 | }), 27 | }), 28 | }); 29 | -------------------------------------------------------------------------------- /src/redux/features/genieSlice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from "@reduxjs/toolkit"; 2 | 3 | interface GenieState { 4 | module?: string | null; 5 | prompt_name: string | null; 6 | msg: any; 7 | } 8 | 9 | const initialState: GenieState = { 10 | module: localStorage.getItem("module"), 11 | prompt_name: null, 12 | msg: {}, 13 | }; 14 | 15 | export const genieSlice = createSlice({ 16 | name: "genieSlice", 17 | initialState, 18 | reducers: { 19 | setModule: (state, action: PayloadAction) => { 20 | state.module = action.payload; 21 | }, 22 | setPromptName: (state, action: PayloadAction) => { 23 | state.prompt_name = action.payload; 24 | }, 25 | setMsg: (state, action: PayloadAction) => { 26 | state.msg = action.payload; 27 | }, 28 | }, 29 | }); 30 | 31 | export const { setModule, setPromptName, setMsg } = genieSlice.actions; 32 | 33 | export default genieSlice.reducer; 34 | -------------------------------------------------------------------------------- /src/redux/features/userSlice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from "@reduxjs/toolkit"; 2 | import { IUser } from "../api/types"; 3 | 4 | interface IUserState { 5 | user: IUser | null; 6 | } 7 | 8 | const initialState: IUserState = { 9 | user: null, 10 | }; 11 | 12 | export const userSlice = createSlice({ 13 | initialState, 14 | name: "userSlice", 15 | reducers: { 16 | logout: () => initialState, 17 | setUser: (state, action: PayloadAction) => { 18 | state.user = action.payload; 19 | }, 20 | }, 21 | }); 22 | 23 | export default userSlice.reducer; 24 | 25 | export const { logout, setUser } = userSlice.actions; 26 | -------------------------------------------------------------------------------- /src/redux/store.ts: -------------------------------------------------------------------------------- 1 | import { configureStore } from "@reduxjs/toolkit"; 2 | import { TypedUseSelectorHook, useSelector, useDispatch } from "react-redux"; 3 | import { productApi } from "./api/productApi"; 4 | import { promptApi } from "./api/promptApi"; 5 | import { authApi } from "./api/authApi"; 6 | import genieReducer from "./features/genieSlice"; 7 | import userReducer from "./features/userSlice"; 8 | import { userApi } from "./api/userApi"; 9 | import { genieApi } from "./api/genieApi"; 10 | 11 | export const store = configureStore({ 12 | reducer: { 13 | [authApi.reducerPath]: authApi.reducer, 14 | [productApi.reducerPath]: productApi.reducer, 15 | [promptApi.reducerPath]: promptApi.reducer, 16 | [userApi.reducerPath]: userApi.reducer, 17 | [genieApi.reducerPath]: genieApi.reducer, 18 | genieState: genieReducer, 19 | userState: userReducer, 20 | }, 21 | devTools: process.env.NODE_ENV === "development", 22 | middleware: (getDefaultMiddleware) => 23 | getDefaultMiddleware({}).concat([ 24 | productApi.middleware, 25 | promptApi.middleware, 26 | authApi.middleware, 27 | userApi.middleware, 28 | genieApi.middleware, 29 | ]), 30 | }); 31 | 32 | export type RootState = ReturnType; 33 | export type AppDispatch = typeof store.dispatch; 34 | export const useAppDispatch = () => useDispatch(); 35 | export const useAppSelector: TypedUseSelectorHook = useSelector; 36 | -------------------------------------------------------------------------------- /src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /src/theme.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | import { PaletteMode } from "@mui/material"; 3 | import { TypographyOptions } from "@mui/material/styles/createTypography"; 4 | 5 | export const ColorModeContext = createContext({ toggleColorMode: () => {} }); 6 | 7 | export const getDesignTokens = (mode: PaletteMode) => ({ 8 | palette: { 9 | mode, 10 | ...(mode === "light" 11 | ? { 12 | primary: { 13 | main: "#4BA5EB", 14 | contrastText: "#fff", 15 | }, 16 | text: { 17 | primary: "#475569", 18 | secondary: "#1D252D", 19 | }, 20 | background: { 21 | default: "#F9FBFC", 22 | paper: "#F8FBFF", 23 | }, 24 | divider: "#E3E3E3", 25 | } 26 | : { 27 | primary: { 28 | main: "#4BA5EB", 29 | }, 30 | background: { 31 | default: "#131825", 32 | paper: "#1C212E", 33 | }, 34 | text: { 35 | primary: "#CBCBCB", 36 | secondary: "#FFFFFF", 37 | }, 38 | divider: "#737373", 39 | }), 40 | }, 41 | typography: { 42 | h4: { 43 | fontWeight: 700, 44 | }, 45 | h6: { 46 | fontSize: 16, 47 | }, 48 | button: { 49 | textTransform: "none", 50 | }, 51 | } as TypographyOptions, 52 | }); 53 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | --------------------------------------------------------------------------------