├── .env.dev ├── .gitignore ├── .husky └── pre-commit ├── .prettierignore ├── .prettierrc ├── .vscode └── settings.json ├── README.md ├── categoryImages ├── Carpenting.png ├── Decor.png ├── Electric.png ├── FreeGirlsOnRent.png ├── Painting.png ├── Paintingaaa.png ├── Paintingssss.png ├── Plumbing.png ├── Testing.png ├── Transport.png └── asdsdsa.png ├── client ├── .babelrc ├── .gitignore ├── .prettierignore ├── .prettierrc ├── eslint.config.mjs ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── images │ │ ├── default-placeholder.png │ │ ├── login-page-2.jpg │ │ ├── login-page.jpg │ │ ├── register_bg_2.png │ │ ├── team_01.jpg │ │ └── team_06.jpg │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt ├── src │ ├── Context │ │ └── GlobalContext.jsx │ ├── Layout │ │ ├── Logo.jsx │ │ ├── PublicLayout.jsx │ │ └── index.jsx │ ├── api.js │ ├── config │ │ ├── consts.js │ │ └── index.js │ ├── dispatcher │ │ └── dispatchers.js │ ├── images │ │ ├── default-placeholder.png │ │ ├── login-page-2.jpg │ │ ├── login-page.jpg │ │ ├── register_bg_2.png │ │ ├── team_01.jpg │ │ └── team_06.jpg │ ├── index.css │ ├── index.jsx │ ├── pages │ │ ├── AddCategory │ │ │ └── index.jsx │ │ ├── AddService │ │ │ └── index.jsx │ │ ├── AllCategory │ │ │ └── index.jsx │ │ ├── AllService │ │ │ ├── columns.jsx │ │ │ └── index.jsx │ │ ├── CategoryChart │ │ │ └── index.jsx │ │ ├── ChangePassword │ │ │ └── index.jsx │ │ ├── ForgotPassword │ │ │ └── index.jsx │ │ ├── Login │ │ │ └── index.jsx │ │ ├── Registration │ │ │ └── index.jsx │ │ ├── ServiceChart │ │ │ └── index.jsx │ │ ├── ServiceDetail │ │ │ ├── Address.jsx │ │ │ ├── MainDetails.jsx │ │ │ ├── OtherInfo.jsx │ │ │ ├── OwnerInfo.jsx │ │ │ └── index.jsx │ │ ├── Spinner │ │ │ ├── index.jsx │ │ │ └── spinner.module.css │ │ └── VerifyOtp │ │ │ └── index.jsx │ ├── routes.jsx │ └── utils │ │ ├── auth.js │ │ └── paramsConvert.js └── tailwind.config.js ├── config.js ├── consts.js ├── controller ├── category.js ├── city.js ├── college.js ├── logincontrol.js ├── service.js ├── state.js └── student.js ├── helper ├── errorHandler.js └── successHandler.js ├── index.js ├── package-lock.json ├── package.json ├── render.yaml ├── router ├── category.js ├── city.js ├── college.js ├── login.js ├── service.js ├── state.js └── student.js └── schemas ├── Address.js ├── Category.js ├── City.js ├── College.js ├── Country.js ├── Merchant.js ├── NewServices.js ├── Services.js ├── State.js ├── Student.js ├── Tags.js └── UserModel.js /.env.dev: -------------------------------------------------------------------------------- 1 | PORT = 5000 2 | DB_STRING = 3 | NODE_ENV = development 4 | API_VERSION = /v1 5 | SECREATE = 6 | GMAIL = 7 | GMAIL_PASSWORD = -------------------------------------------------------------------------------- /.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 | /credentia.js 11 | 12 | # production 13 | /build 14 | 15 | # misc 16 | .env 17 | .DS_Store 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .vercel 27 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm run pretty 5 | npm run pretty --prefix client 6 | npm run lint --prefix client 7 | 8 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/client/node_modules 3 | **/client/build 4 | **/client/public -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 80, 4 | "tabWidth": 2, 5 | "useTabs": false, 6 | "semi": true, 7 | "trailingComma": "all", 8 | "bracketSpacing": true, 9 | "arrowParens": "always", 10 | "endOfLine": "auto", 11 | "jsxSingleQuote": true, 12 | "proseWrap": "preserve", 13 | "quoteProps": "as-needed" 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Final Year Project 2 | 3 | ## Project Overview 4 | The **Home Service Provider** project refers to the facilities-based carrier or reseller with whom the customer contracts for the provision of mobile telecommunications services. Users can create an account and log in. They can use our app's chart visualisation to find the best services near them or around the world, as well as different types of categories.  5 | 6 | ## [Live](https://home-service.onrender.com/) demo 7 | *Please note:* Website loading times may vary. While waiting, you can explore our new blog website platform [AllBlogs](https://allblogs.in). 8 | 9 | 10 | ### Technology Overview 11 | 12 | The Home Service Provider project employs a modern technology stack to deliver an efficient and user-friendly experience. Here's a brief overview of the key technologies and libraries used: 13 | 14 | - **Frontend**: 15 | - **React**: The frontend is built using React, a popular JavaScript library for building user interfaces. React's component-based architecture allows for better code organization and reusability. 16 | - **Ant Design and Tailwind CSS**: We utilize Ant Design and Tailwind CSS frameworks for UI components and styling. These frameworks provide a wide range of pre-built components and utility classes, allowing for rapid development and consistent design. 17 | - **Recharts**: For data visualization, we utilize Recharts, a composable charting library built on React components. It enables us to create interactive and visually appealing charts to present data to users. 18 | 19 | - **Backend**: 20 | - **Express.js**: The backend server is built using Express.js, a fast and minimalist web framework for Node.js. Express simplifies the process of building robust APIs and handling HTTP requests. 21 | - **MongoDB**: We use MongoDB, a NoSQL database, for storing and managing data. Its flexible schema and scalability make it ideal for handling various types of data in our application. 22 | - **Mongoose**: Mongoose is an elegant MongoDB object modeling tool for Node.js. It provides a straightforward way to define schemas and interact with MongoDB databases in our Node.js applications. 23 | 24 | - **Other**: 25 | - **Connected React Router**: This library helps in managing routing and navigation in our React application, ensuring a smooth and seamless user experience. 26 | - **dotenv**: dotenv is used for loading environment variables from a .env file, enabling better configuration management and security. 27 | 28 | Our technology stack is carefully chosen to optimize performance, scalability, and maintainability, ensuring a seamless experience for our users. 29 | 30 | #### Other library and modules 31 | ##### Frontend 32 | 33 | ![](https://img.shields.io/badge/Framework-Tailwind-pink) 34 | ![](https://img.shields.io/badge/Framework-Antd-blue) 35 | ![](https://img.shields.io/badge/Framework-ReactStrap-blue) 36 | ![](https://img.shields.io/badge/Framework-Bootstrap-blue) 37 | ![](https://img.shields.io/badge/Chart-rechart-pink) 38 | ![](https://img.shields.io/badge/Router-connected_react_router-green) 39 | 40 | ##### Backend 41 | 42 | ![](https://img.shields.io/badge/Framework-Express-blue) 43 | ![](https://img.shields.io/badge/Env-dotenv-red) 44 | ![](https://img.shields.io/badge/Middleware-mongoose-pink) 45 | ![](https://img.shields.io/badge/Middleware-cors-pink) 46 | ![](https://img.shields.io/badge/server-nodemon-orange) 47 | 48 | ### Getting Started 49 | - #### Installation 50 | Use `npm install` or `yarn install` to install dependencies. 51 | 52 | - #### Start Frontend 53 | Use `npm run start:client` or `yarn run start:client` to start the frontend. 54 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 55 | 56 | - #### Start Backend 57 | To start the backend server, follow these steps: 58 | 59 | 1. **Environment Setup**: 60 | - Create a `.env` file in the root directory of your project. 61 | - Add the following environment variables to the `.env` file: 62 | 63 | ```plaintext 64 | PORT=5000 65 | DB_STRING= 66 | NODE_ENV=development 67 | API_VERSION=/v1 68 | SECREATE= 69 | GMAIL= 70 | GMAIL_PASSWORD= 71 | ``` 72 | 73 | Replace ``, ``, ``, and `` with your actual database connection string, secret key, Gmail address, and Gmail password respectively. 74 | 75 | 2. **Dependencies Installation**: 76 | - Run `npm install` or `yarn install` to install the required dependencies. 77 | 78 | 3. **Starting the Server**: 79 | - Run `npm run start:dev` or `yarn run start:dev` to start the backend server. 80 | - This command will use Nodemon to watch for changes and restart the server automatically. 81 | - The server will be running on [http://localhost:5000](http://localhost:5000). 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /categoryImages/Carpenting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/categoryImages/Carpenting.png -------------------------------------------------------------------------------- /categoryImages/Decor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/categoryImages/Decor.png -------------------------------------------------------------------------------- /categoryImages/Electric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/categoryImages/Electric.png -------------------------------------------------------------------------------- /categoryImages/FreeGirlsOnRent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/categoryImages/FreeGirlsOnRent.png -------------------------------------------------------------------------------- /categoryImages/Painting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/categoryImages/Painting.png -------------------------------------------------------------------------------- /categoryImages/Paintingaaa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/categoryImages/Paintingaaa.png -------------------------------------------------------------------------------- /categoryImages/Paintingssss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/categoryImages/Paintingssss.png -------------------------------------------------------------------------------- /categoryImages/Plumbing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/categoryImages/Plumbing.png -------------------------------------------------------------------------------- /categoryImages/Testing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/categoryImages/Testing.png -------------------------------------------------------------------------------- /categoryImages/Transport.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/categoryImages/Transport.png -------------------------------------------------------------------------------- /categoryImages/asdsdsa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/categoryImages/asdsdsa.png -------------------------------------------------------------------------------- /client/.babelrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | { 4 | "presets": [ 5 | "@babel/preset-env", 6 | "@babel/preset-react" 7 | ], 8 | "plugins": [ 9 | "@babel/plugin-proposal-private-property-in-object" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /client/.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 | node_modules/ 9 | /build/* 10 | 11 | # testing 12 | /coverage 13 | 14 | # production 15 | /build 16 | 17 | # misc 18 | .DS_Store 19 | .env.local 20 | .env.development.local 21 | .env.test.local 22 | .env.production.local 23 | 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | -------------------------------------------------------------------------------- /client/.prettierignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/build 3 | **/public -------------------------------------------------------------------------------- /client/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 80, 4 | "tabWidth": 2, 5 | "useTabs": false, 6 | "semi": true, 7 | "trailingComma": "all", 8 | "bracketSpacing": true, 9 | "arrowParens": "always", 10 | "endOfLine": "auto", 11 | "jsxSingleQuote": true, 12 | "proseWrap": "preserve", 13 | "quoteProps": "as-needed" 14 | } 15 | -------------------------------------------------------------------------------- /client/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from 'globals'; 2 | import pluginJs from '@eslint/js'; 3 | import pluginReactConfig from 'eslint-plugin-react/configs/recommended.js'; 4 | export default [ 5 | { 6 | languageOptions: { 7 | ecmaVersion: 2023, 8 | sourceType: 'module', 9 | globals: { 10 | ...globals.browser, 11 | myCustomGlobal: 'readonly', 12 | }, 13 | }, 14 | }, 15 | pluginJs.configs.recommended, 16 | pluginReactConfig, 17 | { 18 | ignores: ['build/**'], 19 | rules: { 20 | 'react-hooks/exhaustive-deps': 'off', 21 | 'react/prop-types': 'off', 22 | 'react/jsx-filename-extension': [ 23 | 'warn', 24 | { 25 | extensions: ['.jsx', '.tsx'], 26 | }, 27 | ], 28 | 'react/jsx-props-no-spreading': 'warn', 29 | 'react/function-component-definition': 'off', 30 | 'no-underscore-dangle': 'warn', 31 | 'react/jsx-key': 'warn', 32 | 'no-shadow': 'warn', 33 | 'no-unused-vars': 'warn', 34 | 'react/destructuring-assignment': 'warn', 35 | 'react/jsx-no-useless-fragment': 'warn', 36 | 'react/no-unstable-nested-components': 'warn', 37 | 'react/no-unescaped-entities': 'warn', 38 | 'react/no-array-index-key': 'warn', 39 | 'no-undef': 'error', 40 | }, 41 | 42 | settings: { 43 | react: { 44 | version: 'detect', 45 | }, 46 | }, 47 | }, 48 | ]; 49 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "home-service-provider-client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@ant-design/icons": "^5.3.6", 7 | "@ant-design/pro-components": "^2.7.1", 8 | "@babel/eslint-parser": "^7.24.5", 9 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11", 10 | "@tailwindcss/aspect-ratio": "^0.4.2", 11 | "@tailwindcss/forms": "^0.2.1", 12 | "@tailwindcss/typography": "^0.5.13", 13 | "@testing-library/jest-dom": "^6.4.2", 14 | "@testing-library/react": "^15.0.5", 15 | "@testing-library/user-event": "^14.5.2", 16 | "antd": "^5.16.5", 17 | "axios": "^1.6.8", 18 | "babel-eslint": "^10.1.0", 19 | "eslint-config-airbnb": "^19.0.4", 20 | "react": "^18.3.1", 21 | "react-custom-scrollbars-2": "^4.5.0", 22 | "react-dom": "^18.3.1", 23 | "react-ga4": "^2.1.0", 24 | "react-redux": "^9.1.1", 25 | "react-router-dom": "^6.23.0", 26 | "react-scripts": "^5.0.1", 27 | "recharts": "^2.12.6", 28 | "redux": "^5.0.1", 29 | "redux-saga": "^1.3.0", 30 | "tailwindcss": "^3.3.5", 31 | "web-vitals": "^3.5.2" 32 | }, 33 | "scripts": { 34 | "start": "react-scripts start", 35 | "build": "react-scripts build", 36 | "test": "react-scripts test", 37 | "eject": "react-scripts eject", 38 | "lint": "eslint . ", 39 | "lint-fix": "eslint . --fix", 40 | "pretty": "prettier --write \"**/*.js\" \"**/*.jsx\"" 41 | }, 42 | "eslintConfig": { 43 | "extends": [ 44 | "react-app", 45 | "react-app/jest" 46 | ] 47 | }, 48 | "browserslist": { 49 | "production": [ 50 | ">0.2%", 51 | "not dead", 52 | "not op_mini all" 53 | ], 54 | "development": [ 55 | "last 1 chrome version", 56 | "last 1 firefox version", 57 | "last 1 safari version" 58 | ] 59 | }, 60 | "engines": { 61 | "node": "v20.11.1", 62 | "npm": "10.2.4" 63 | }, 64 | "devDependencies": { 65 | "@babel/preset-env": "^7.24.5", 66 | "@babel/preset-react": "^7.24.1", 67 | "@eslint/eslintrc": "^3.0.2", 68 | "@eslint/js": "^9.1.1", 69 | "eslint": "^8.57.0", 70 | "eslint-config-prettier": "^9.1.0", 71 | "eslint-plugin-prettier": "^5.1.3", 72 | "eslint-plugin-react": "^7.34.1", 73 | "globals": "^15.1.0", 74 | "prettier": "^3.2.5" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/images/default-placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/client/public/images/default-placeholder.png -------------------------------------------------------------------------------- /client/public/images/login-page-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/client/public/images/login-page-2.jpg -------------------------------------------------------------------------------- /client/public/images/login-page.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/client/public/images/login-page.jpg -------------------------------------------------------------------------------- /client/public/images/register_bg_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/client/public/images/register_bg_2.png -------------------------------------------------------------------------------- /client/public/images/team_01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/client/public/images/team_01.jpg -------------------------------------------------------------------------------- /client/public/images/team_06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/client/public/images/team_06.jpg -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 31 | 32 | 41 | 44 | 48 | Home Services Provider 49 | 50 | 51 | 52 |
53 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/client/public/logo192.png -------------------------------------------------------------------------------- /client/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/client/public/logo512.png -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Home Services", 3 | "name": "Home Services", 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 | -------------------------------------------------------------------------------- /client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client/src/Context/GlobalContext.jsx: -------------------------------------------------------------------------------- 1 | import React, { createContext, useReducer, useMemo } from 'react'; 2 | 3 | const initial = { 4 | loading: true, 5 | isLoggedIn: false, 6 | isMerchant: false, 7 | page: { 8 | login: true, 9 | registration: false, 10 | item: false, 11 | }, 12 | items: [], 13 | error: { 14 | error: false, 15 | msg: '', 16 | }, 17 | success: { 18 | error: false, 19 | msg: '', 20 | }, 21 | user: {}, 22 | categories: [], 23 | states: [], 24 | search: { 25 | name: '', 26 | state: '', 27 | city: '', 28 | category: '', 29 | }, 30 | }; 31 | export const GlobalContext = createContext(initial); 32 | const reducer = (state, action) => { 33 | switch (action.type) { 34 | case 'LOGIN_SUCCESS': 35 | return { 36 | ...state, 37 | isLoggedIn: true, 38 | }; 39 | case 'SET_MERCHANT': 40 | return { 41 | ...state, 42 | isMerchant: action.isMerchant, 43 | }; 44 | case 'LOGOUT': 45 | localStorage.removeItem('auth-token'); 46 | localStorage.removeItem('user_id'); 47 | return { 48 | ...state, 49 | page: { login: true, registration: false, item: false }, 50 | user: {}, 51 | isLoggedIn: false, 52 | isMerchant: false, 53 | }; 54 | case 'SET_SEARCH_PARAMS': 55 | return { 56 | ...state, 57 | search: action.payload, 58 | }; 59 | case 'SET_CATEGORIES': 60 | return { 61 | ...state, 62 | categories: action.payload, 63 | }; 64 | case 'SET_STATES': 65 | return { 66 | ...state, 67 | states: action.payload, 68 | }; 69 | case 'SET_USER': 70 | return { 71 | ...state, 72 | user: action.payload, 73 | }; 74 | case 'SET_HOBBI': 75 | return { 76 | ...state, 77 | items: action.payload, 78 | loading: false, 79 | }; 80 | case 'DELETE_HOBBI': 81 | return { 82 | ...state, 83 | // eslint-disable-next-line no-underscore-dangle 84 | items: state.items.filter((item) => item._id !== action.payload), 85 | }; 86 | case 'ADD_HOBBI': 87 | return { 88 | ...state, 89 | items: [...state.items, action.payload], 90 | }; 91 | case 'REGISTRACTIONPAGE': 92 | return { 93 | ...state, 94 | page: { login: false, registration: true, item: false }, 95 | }; 96 | case 'ITEMPAGE': 97 | return { 98 | ...state, 99 | page: { login: false, registration: false, item: true }, 100 | }; 101 | case 'LOGINPAGE': 102 | return { 103 | ...state, 104 | page: { login: true, registration: false, item: false }, 105 | }; 106 | 107 | case 'ERROR': 108 | return { 109 | ...state, 110 | error: { 111 | error: true, 112 | msg: action.payload, 113 | }, 114 | }; 115 | case 'SUCCESS': 116 | return { 117 | ...state, 118 | success: { 119 | success: true, 120 | msg: action.payload, 121 | }, 122 | }; 123 | case 'CLEAR_ERROR': 124 | return { 125 | ...state, 126 | error: { 127 | error: false, 128 | msg: '', 129 | }, 130 | success: { 131 | success: false, 132 | msg: '', 133 | }, 134 | }; 135 | default: 136 | return state; 137 | } 138 | }; 139 | export function Provider({ children }) { 140 | const [data, dispatch] = useReducer(reducer, initial); 141 | const value = useMemo(() => ({ data, dispatch }), [data, dispatch]); 142 | return ( 143 | {children} 144 | ); 145 | } 146 | -------------------------------------------------------------------------------- /client/src/Layout/Logo.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | class Logo extends React.PureComponent { 4 | render() { 5 | return ( 6 | 14 | 18 | 22 | 26 | 30 | 31 | ); 32 | } 33 | } 34 | 35 | export default Logo; 36 | -------------------------------------------------------------------------------- /client/src/Layout/PublicLayout.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | function PublicLayout({ children }) { 4 | const [showModal, setShowModal] = useState(false); 5 | 6 | useEffect(() => { 7 | const timer = setTimeout(() => { 8 | setShowModal(true); 9 | }, 3000); // Display modal after 3 seconds 10 | 11 | return () => clearTimeout(timer); 12 | }, []); 13 | 14 | return ( 15 |
16 |
17 |
24 |
25 |
26 |
27 |
28 | {children} 29 |
30 |
31 |
32 |
33 |
34 | {showModal && ( 35 |
36 |
37 |
38 |
39 |

40 | Welcome to AllBlogs.in 41 |

42 |

43 | Your go-to platform for the latest blogs and articles! 44 |

45 | 50 |
51 | 57 | 63 | Visit AllBlogs.in 64 | 65 |
66 |

67 | Newly launched website 68 |

69 |
70 |
71 |
72 | )} 73 |
74 | ); 75 | } 76 | 77 | export default PublicLayout; 78 | -------------------------------------------------------------------------------- /client/src/Layout/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useContext, useEffect } from 'react'; 2 | import { Menu, Input, Select, Button, Dropdown } from 'antd'; 3 | import { 4 | DotChartOutlined, 5 | PieChartOutlined, 6 | RadarChartOutlined, 7 | TeamOutlined, 8 | UserOutlined, 9 | ProfileOutlined, 10 | LogoutOutlined, 11 | SettingOutlined, 12 | SearchOutlined, 13 | MenuOutlined, 14 | } from '@ant-design/icons'; 15 | import { Link, useNavigate, useLocation } from 'react-router-dom'; 16 | import Avatar from 'antd/lib/avatar/avatar'; 17 | import axios from 'axios'; 18 | import Logo from './Logo'; 19 | import { GlobalContext } from '../Context/GlobalContext'; 20 | import API from '../api'; 21 | import { getArrayParams, setUrlString } from '../utils/paramsConvert'; 22 | 23 | const { Option } = Select; 24 | 25 | const { SubMenu } = Menu; 26 | 27 | function Index({ children }) { 28 | const [collapsed, setCollapsed] = useState(false); 29 | const { data, dispatch } = useContext(GlobalContext); 30 | const navigate = useNavigate(); 31 | const { search: urlSearch, pathname } = useLocation(); 32 | const [isHide, setIsHide] = useState(false); 33 | 34 | const [search, setSearch] = useState({ 35 | name: '', 36 | state: 'all', 37 | city: '', 38 | category: 'all', 39 | }); 40 | 41 | const fieldOnChange = (key, value) => { 42 | setSearch({ ...search, [key]: value }); 43 | }; 44 | 45 | const toggleCollapsed = () => { 46 | setCollapsed((prevCollapsed) => !prevCollapsed); 47 | }; 48 | 49 | const secondURL = pathname.split('/')[2]; 50 | useEffect(() => { 51 | if (secondURL === 'addCategory' || secondURL === 'addService') 52 | setIsHide(true); 53 | else setIsHide(false); 54 | }, [secondURL]); 55 | useEffect(() => { 56 | setSearch(getArrayParams(urlSearch)); 57 | }, [urlSearch]); 58 | useEffect(() => { 59 | axios 60 | .get(API.categories) 61 | .then((res) => { 62 | if (res.data.success) { 63 | dispatch({ type: 'SET_CATEGORIES', payload: res.data.data }); 64 | } 65 | }) 66 | .catch((e) => console.error(e)); 67 | axios 68 | .get(API.states) 69 | .then((res) => { 70 | if (res.data.success) { 71 | dispatch({ type: 'SET_STATES', payload: res.data.data }); 72 | } 73 | }) 74 | .catch((e) => console.error(e)); 75 | }, [dispatch]); 76 | 77 | const menu = ( 78 | 79 | }> 80 | Profile 81 | 82 | }> 83 | Setting 84 | 85 | } 88 | onClick={() => dispatch({ type: 'LOGOUT' })} 89 | > 90 | Logout 91 | 92 | 93 | ); 94 | 95 | return ( 96 |
97 |
102 |
103 | 104 |
105 | 112 | }> 113 | }> 114 | 115 | All Categories 116 | 117 | 118 | }> 119 | Add Category 120 | 121 | 122 | }> 123 | }> 124 | 125 | All Services 126 | 127 | 128 | 129 | }> 130 | Add Service 131 | 132 | 133 | } title='Charts'> 134 | }> 135 | 136 | Categories charts 137 | 138 | 139 | }> 140 | Services charts 141 | 142 | 143 | 144 |
145 |
146 |
147 | 206 |
207 | )} 208 | 209 | 210 | 211 | 212 |
{children}
213 |
214 | Bhavya Design ©{new Date().getFullYear()} Created by bhavyabhut 215 |
216 |
217 | 218 | ); 219 | } 220 | 221 | export default Index; 222 | -------------------------------------------------------------------------------- /client/src/api.js: -------------------------------------------------------------------------------- 1 | import config from './config'; 2 | 3 | const { API_END_POINT, VERSION } = config; 4 | 5 | const routes = { 6 | students: 'student/', 7 | student: 'student/:studentId', 8 | colleges: 'college/', 9 | college: 'college/:collegeId', 10 | collegeStudent: 'student/college/:collegeId', 11 | collegeByState: 'college/state/:stateId', 12 | collegeByLocation: 'college/location/:locationId', 13 | collegeChart: 'college/chart/pie', 14 | collegeCourseChart: 'college/chart/pie/course', 15 | login: 'login', 16 | registration: 'login/registration', 17 | auth: 'login/auth', 18 | categories: 'categories', 19 | states: 'states', 20 | categoryDashboard: 'categories/dashboard', 21 | services: 'services', 22 | getServiceById: 'services/:serviceId', 23 | servicesChart: 'services/chart', 24 | sendOtp: 'login/otp', 25 | verifyOtp: 'login/verifyOtp', 26 | changePassword: 'login/changePassword', 27 | addCategory: 'categories/addCategory', 28 | categoryImage: 'categoryImages/:id', 29 | addService: 'services/addService', 30 | getAllCity: 'city', 31 | }; 32 | 33 | const API = {}; 34 | Object.keys(routes).forEach((key) => { 35 | API[key] = `${API_END_POINT}${VERSION}/${routes[key]}`; 36 | }); 37 | 38 | export default API; 39 | -------------------------------------------------------------------------------- /client/src/config/consts.js: -------------------------------------------------------------------------------- 1 | const tagColor = [ 2 | '#ffa39e', 3 | '#ffbb96', 4 | '#ffd591', 5 | '#ffe58f', 6 | '#fffb8f', 7 | '#eaff8f', 8 | '#b7eb8f', 9 | '#87e8de', 10 | '#91d5ff', 11 | '#adc6ff', 12 | '#d3adf7', 13 | '#ffadd2', 14 | ]; 15 | export const CHART_COLORS = [ 16 | '#0088FE', 17 | '#00C49F', 18 | '#FFBB28', 19 | '#FF8042', 20 | '#fe0033', 21 | '#005fc4', 22 | '#cb8c02', 23 | '#a7a09b', 24 | '#9652ba', 25 | '#d603e5', 26 | '#57ff28', 27 | '#08cdef', 28 | ]; 29 | export default tagColor; 30 | -------------------------------------------------------------------------------- /client/src/config/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | const config = {}; 3 | 4 | const isDevelopment = process.env.NODE_ENV === 'development'; 5 | 6 | config.NODE_ENV = process.env.NODE_ENV; 7 | config.VERSION = 'v1'; 8 | 9 | config.API_END_POINT = isDevelopment ? 'http://localhost:5000/' : '/'; 10 | 11 | config.GA_KEY = 'G-LBL46ZSPN5'; 12 | 13 | export default config; 14 | -------------------------------------------------------------------------------- /client/src/dispatcher/dispatchers.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { GlobalContext } from '../Context/GlobalContext'; 3 | 4 | export const LoginpageDispathcer = () => { 5 | const { dispatch } = useContext(GlobalContext); 6 | dispatch({ type: 'LOGIN_SUCCESS' }); 7 | }; 8 | 9 | export const LogoutDispathcer = () => { 10 | const { dispatch } = useContext(GlobalContext); 11 | dispatch({ type: 'LOGOUT' }); 12 | }; 13 | 14 | export const SetSearchParameters = (payload) => { 15 | const { dispatch } = useContext(GlobalContext); 16 | dispatch({ type: 'SET_SEARCH_PARAMS', payload }); 17 | }; 18 | 19 | export const SetCategories = (payload) => { 20 | const { dispatch } = useContext(GlobalContext); 21 | dispatch({ type: 'SET_CATEGORIES', payload }); 22 | }; 23 | 24 | export const SetStates = (payload) => { 25 | const { dispatch } = useContext(GlobalContext); 26 | dispatch({ type: 'SET_STATES', payload }); 27 | }; 28 | 29 | export const ErrorDispathcer = (payload) => { 30 | const { dispatch } = useContext(GlobalContext); 31 | dispatch({ type: 'ERROR', payload }); 32 | }; 33 | 34 | export const RegistrationpageDispathcer = () => { 35 | const { dispatch } = useContext(GlobalContext); 36 | dispatch({ type: 'REGISTRACTIONPAGE' }); 37 | }; 38 | 39 | export const AddhobbiDispathcer = (payload) => { 40 | const { dispatch } = useContext(GlobalContext); 41 | dispatch({ type: 'ADD_HOBBI', payload }); 42 | }; 43 | export const SetbobbiDispathcer = (payload) => { 44 | const { dispatch } = useContext(GlobalContext); 45 | dispatch({ type: 'SET_HOBBI', payload }); 46 | }; 47 | export const DeletebobbiDispathcer = (payload) => { 48 | const { dispatch } = useContext(GlobalContext); 49 | dispatch({ type: 'DELETE_HOBBI', payload }); 50 | }; 51 | export const SetuserDispathcer = (payload) => { 52 | const { dispatch } = useContext(GlobalContext); 53 | dispatch({ type: 'SET_USER', payload }); 54 | }; 55 | export const ClearDispathcer = () => { 56 | const { dispatch } = useContext(GlobalContext); 57 | dispatch({ type: 'CLEAR' }); 58 | }; 59 | -------------------------------------------------------------------------------- /client/src/images/default-placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/client/src/images/default-placeholder.png -------------------------------------------------------------------------------- /client/src/images/login-page-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/client/src/images/login-page-2.jpg -------------------------------------------------------------------------------- /client/src/images/login-page.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/client/src/images/login-page.jpg -------------------------------------------------------------------------------- /client/src/images/register_bg_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/client/src/images/register_bg_2.png -------------------------------------------------------------------------------- /client/src/images/team_01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/client/src/images/team_01.jpg -------------------------------------------------------------------------------- /client/src/images/team_06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavyabhut/home-service-provider/b35aa61f520ab76caad426f9302a48650e985c14/client/src/images/team_06.jpg -------------------------------------------------------------------------------- /client/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | body { 6 | margin: 0; 7 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 8 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 9 | sans-serif; 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | } 13 | 14 | code { 15 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 16 | monospace; 17 | } 18 | .logo-svg { 19 | max-width: 100px; 20 | } 21 | #components-layout-demo-side .logo { 22 | height: 32px; 23 | margin: 16px; 24 | background: rgba(255, 255, 255, 0.3); 25 | } 26 | 27 | .site-layout .site-layout-background { 28 | background: #fff; 29 | } 30 | .site-layout-background { 31 | /* height: 100%; */ 32 | } 33 | .text-dark { 34 | color: black; 35 | font-weight: bold; 36 | margin-right: 1.5rem; 37 | } 38 | .ant-card { 39 | border-radius: 5px !important; 40 | background-color: white; 41 | margin: 0.5rem 0.1rem !important; 42 | box-shadow: 43 | 0 4px 10px 0 #6c757d6e, 44 | 0 0 10px 0 #f8f8f9 !important; 45 | overflow: hidden; 46 | word-break: break-word; 47 | } 48 | .locationCity::placeholder { 49 | color: #f0f0f0 !important; 50 | opacity: 1 !important; /* Firefox */ 51 | } 52 | 53 | .locationCity::-ms-input-placeholder { 54 | /* Edge 12 -18 */ 55 | color: #f0f0f0 !important; 56 | } 57 | 58 | .collapsed { 59 | width: 80px; /* Adjust as needed */ 60 | overflow-x: hidden; 61 | transition: width 0.2s ease; /* Add transition for smooth animation */ 62 | } 63 | 64 | .recharts-legend-wrapper { 65 | position: inherit !important; 66 | } 67 | 68 | .customRow { 69 | margin: 1rem 0; 70 | } 71 | .comingsoon { 72 | height: 100%; 73 | width: 100%; 74 | display: flex; 75 | justify-content: center; 76 | align-items: center; 77 | font-size: 52px; 78 | background: -webkit-linear-gradient( 79 | #1890ff, 80 | #661a73, 81 | rgb(239, 31, 38), 82 | #a2a275 83 | ); 84 | -webkit-background-clip: text; 85 | -webkit-text-fill-color: transparent; 86 | } 87 | .signin { 88 | display: flex; 89 | -webkit-box-align: center; 90 | align-items: center; 91 | flex-wrap: wrap; 92 | } 93 | 94 | .signin-form { 95 | width: 40%; 96 | padding: 2.5rem 5.5rem 0rem 5.5rem; 97 | /* padding-top: 4rem; */ 98 | } 99 | .signin-image-div { 100 | background: url('images/login-page-2.jpg'); 101 | background-position: center center; 102 | height: 100vh; 103 | background-size: cover; 104 | width: 60%; 105 | position: fixed; 106 | top: 0; 107 | right: 0; 108 | } 109 | 110 | .title { 111 | /* color: #ffd200; */ 112 | /* font-size: large; */ 113 | margin-bottom: 0 !important; 114 | background: linear-gradient(to right, #ffd200, #06e07f, #e3073c, #1f84ef); 115 | -webkit-background-clip: text; 116 | -webkit-text-fill-color: transparent; 117 | text-transform: capitalize; 118 | font-size: 35px; 119 | font-weight: 700; 120 | font-family: Lato, sans-serif; 121 | -webkit-font-smoothing: antialiased; 122 | text-shadow: rgb(0 0 0 / 0%) 1px 1px 1px; 123 | -webkit-tap-highlight-color: transparent; 124 | } 125 | 126 | .welcomeBack { 127 | color: rgb(44, 44, 44); 128 | font-size: 45px; 129 | line-height: 54px; 130 | font-weight: 700; 131 | margin-bottom: 0px; 132 | margin-top: 18px; 133 | font-size: 28px; 134 | line-height: 32px; 135 | font-family: Lato, sans-serif; 136 | -webkit-font-smoothing: antialiased; 137 | text-shadow: rgb(0 0 0 / 0%) 1px 1px 1px; 138 | -webkit-tap-highlight-color: transparent; 139 | } 140 | 141 | .loginIntoAccount { 142 | font-size: 18px; 143 | line-height: 30px; 144 | margin-bottom: 25px; 145 | margin-top: 10px; 146 | font-family: Lato, sans-serif; 147 | -webkit-font-smoothing: antialiased; 148 | text-shadow: rgb(0 0 0 / 0%) 1px 1px 1px; 149 | -webkit-tap-highlight-color: transparent; 150 | } 151 | 152 | .singInLabel { 153 | display: block; 154 | color: rgb(44, 44, 44); 155 | font-size: 15px; 156 | line-height: 18px; 157 | font-weight: 600; 158 | margin-bottom: 15px; 159 | } 160 | 161 | .remeberMe { 162 | font-size: 15px; 163 | line-height: 1; 164 | font-weight: 700; 165 | color: rgb(44, 44, 44); 166 | padding: 0px 8px; 167 | font-family: Lato, sans-serif; 168 | -webkit-font-smoothing: antialiased; 169 | text-shadow: rgb(0 0 0 / 0%) 1px 1px 1px; 170 | -webkit-tap-highlight-color: transparent; 171 | } 172 | 173 | .locationState { 174 | background-color: rgb(0, 21, 41) !important; 175 | color: white; 176 | } 177 | 178 | .locationCity { 179 | background-color: rgb(0, 21, 41) !important; 180 | color: white; 181 | } 182 | 183 | .locationState div:first-child span:first-child input:first-child { 184 | background-color: rgb(0, 21, 41) !important; 185 | color: white; 186 | } 187 | .locationState span:first-child, 188 | .locationState div { 189 | background-color: rgb(0, 21, 41) !important; 190 | color: white; 191 | } 192 | 193 | .locationState div:first-child { 194 | background-color: rgb(0, 21, 41) !important; 195 | color: white; 196 | } 197 | 198 | .locationState span:nth-child(1) { 199 | /* background-color: rgb(0, 21, 41) !important; */ 200 | color: white; 201 | } 202 | 203 | .locationCity input { 204 | background-color: rgb(0, 21, 41) !important; 205 | color: white; 206 | } 207 | 208 | .mainSearchButton:hover { 209 | transform: scale(1.1); 210 | } 211 | .CustomCARD { 212 | height: 185px !important; 213 | width: 100% !important; 214 | background-repeat: no-repeat !important; 215 | background-size: cover !important; 216 | background-position: center center !important; 217 | } 218 | 219 | .bg-blueGray-50 { 220 | --tw-bg-opacity: 1; 221 | background-color: rgba(248, 250, 252, var(--tw-bg-opacity)); 222 | } 223 | 224 | .bg-blueGray-100 { 225 | --tw-bg-opacity: 1; 226 | background-color: rgba(241, 245, 249, var(--tw-bg-opacity)); 227 | } 228 | 229 | .bg-blueGray-200 { 230 | --tw-bg-opacity: 1; 231 | background-color: rgba(226, 232, 240, var(--tw-bg-opacity)); 232 | } 233 | 234 | .bg-blueGray-600 { 235 | --tw-bg-opacity: 1; 236 | background-color: rgba(71, 85, 105, var(--tw-bg-opacity)); 237 | } 238 | 239 | .bg-blueGray-700 { 240 | --tw-bg-opacity: 1; 241 | background-color: rgba(51, 65, 85, var(--tw-bg-opacity)); 242 | } 243 | 244 | .bg-blueGray-800 { 245 | --tw-bg-opacity: 1; 246 | background-color: rgba(30, 41, 59, var(--tw-bg-opacity)); 247 | } 248 | 249 | .active\:bg-blueGray-50:active { 250 | --tw-bg-opacity: 1; 251 | background-color: rgba(248, 250, 252, var(--tw-bg-opacity)); 252 | } 253 | 254 | .active\:bg-blueGray-600:active { 255 | --tw-bg-opacity: 1; 256 | background-color: rgba(71, 85, 105, var(--tw-bg-opacity)); 257 | } 258 | 259 | .border-blueGray-50 { 260 | --tw-border-opacity: 1; 261 | border-color: rgba(248, 250, 252, var(--tw-border-opacity)); 262 | } 263 | 264 | .border-blueGray-100 { 265 | --tw-border-opacity: 1; 266 | border-color: rgba(241, 245, 249, var(--tw-border-opacity)); 267 | } 268 | 269 | .border-blueGray-200 { 270 | --tw-border-opacity: 1; 271 | border-color: rgba(226, 232, 240, var(--tw-border-opacity)); 272 | } 273 | 274 | .border-blueGray-300 { 275 | --tw-border-opacity: 1; 276 | border-color: rgba(203, 213, 225, var(--tw-border-opacity)); 277 | } 278 | 279 | .border-blueGray-500 { 280 | --tw-border-opacity: 1; 281 | border-color: rgba(100, 116, 139, var(--tw-border-opacity)); 282 | } 283 | 284 | .border-blueGray-600 { 285 | --tw-border-opacity: 1; 286 | border-color: rgba(71, 85, 105, var(--tw-border-opacity)); 287 | } 288 | -------------------------------------------------------------------------------- /client/src/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactGA from 'react-ga4'; 3 | import { createRoot } from 'react-dom/client'; 4 | 5 | import PublicRoutes from './routes'; 6 | import { Provider } from './Context/GlobalContext'; 7 | import config from './config'; 8 | 9 | import './index.css'; 10 | 11 | ReactGA.initialize(config.GA_KEY); 12 | ReactGA.send({ 13 | hitType: 'pageview', 14 | page: window.location.pathname, 15 | title: window.location.pathname, 16 | }); 17 | 18 | const container = document.getElementById('root'); 19 | const root = createRoot(container); 20 | 21 | root.render( 22 | 23 | 24 | 25 | 26 | , 27 | ); 28 | -------------------------------------------------------------------------------- /client/src/pages/AddCategory/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Upload, Button, Input, notification, Form } from 'antd'; 3 | import { PlusOutlined } from '@ant-design/icons'; 4 | import { Link, useNavigate } from 'react-router-dom'; 5 | import axios from 'axios'; 6 | import API from '../../api'; 7 | 8 | function getBase64(file) { 9 | if (file) 10 | return new Promise((resolve, reject) => { 11 | const reader = new FileReader(); 12 | reader.readAsDataURL(file); 13 | reader.onload = () => resolve(reader.result); 14 | reader.onerror = (error) => reject(error); 15 | }); 16 | return new Promise((resolve, reject) => { 17 | reject(); 18 | }); 19 | } 20 | 21 | export default function AddCategory() { 22 | const [loader, setLoader] = useState(false); 23 | const [fieldData, setFieldData] = useState({ name: '', description: '' }); 24 | const [file, setFile] = useState(); 25 | const [url, setUrl] = useState(); 26 | const navigate = useNavigate(); 27 | const [state, setState] = useState({ 28 | loader: false, 29 | error: false, 30 | message: '', 31 | }); 32 | 33 | const dummyRequest = ({ onSuccess }) => { 34 | setTimeout(() => { 35 | onSuccess('ok'); 36 | }, 0); 37 | }; 38 | 39 | const sendData = () => { 40 | if (!fieldData.name.trim()) { 41 | setState({ 42 | ...state, 43 | error: true, 44 | message: 'Please enter category name', 45 | }); 46 | return; 47 | } 48 | if (!file) { 49 | setState({ 50 | ...state, 51 | error: true, 52 | message: 'Please select image for category', 53 | }); 54 | return; 55 | } 56 | setLoader(true); 57 | const formData = new FormData(); 58 | formData.append('name', fieldData.name); 59 | formData.append('image', file); 60 | formData.append('description', fieldData.description); 61 | axios 62 | .post(API.addCategory, formData) 63 | .then((res) => { 64 | if (res.data.success) { 65 | navigate( 66 | '/home-services/allCategories?category=all&state=all&city=&name=', 67 | ); 68 | } 69 | setLoader(false); 70 | }) 71 | .catch((e) => { 72 | if (e.response) { 73 | setState({ error: true, message: e.response.data.error }); 74 | } else { 75 | setState({ error: true, message: 'Server Error' }); 76 | } 77 | setLoader(false); 78 | }); 79 | }; 80 | 81 | useEffect(() => { 82 | if (state.error) { 83 | notification.open({ 84 | message: state.message, 85 | type: 'error', 86 | }); 87 | setState({ ...state, error: false }); 88 | } 89 | }, [state.error]); 90 | 91 | return ( 92 |
93 |
94 |

Add Category

95 |

Please add category in home service

96 |
97 | 106 | { 112 | setFile(event.file.originFileObj); 113 | getBase64(event.file.originFileObj) 114 | .then((data) => setUrl(data)) 115 | .catch((e) => console.log(e)); 116 | }} 117 | name='file' 118 | > 119 | {url ? ( 120 | avatar 121 | ) : ( 122 |
123 | 124 |
Upload
125 |
126 | )} 127 |
128 |
129 | 138 | 141 | setFieldData({ ...fieldData, name: e.target.value }) 142 | } 143 | placeholder='Category Name' 144 | /> 145 | 146 | 147 | 150 | setFieldData({ 151 | ...fieldData, 152 | description: e.target.value, 153 | }) 154 | } 155 | placeholder='Category Description' 156 | /> 157 | 158 | 168 |
169 | {state.error && ( 170 |
{state.message}
171 | )} 172 |

173 | Find existing category? 174 | 175 | All Category 176 | 177 |

178 |
179 |
180 | ); 181 | } 182 | -------------------------------------------------------------------------------- /client/src/pages/AddService/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Input, Button, Select, Row, Col, Collapse, notification } from 'antd'; 3 | import axios from 'axios'; 4 | import { useNavigate } from 'react-router-dom'; 5 | 6 | import API from '../../api'; 7 | 8 | const { Panel } = Collapse; 9 | const { Option } = Select; 10 | 11 | export default function AddServices() { 12 | const [field, setField] = useState({}); 13 | const navigate = useNavigate(); 14 | const [loader, setLoader] = useState(false); 15 | 16 | const [categoryOption, setCategoryOption] = useState([]); 17 | const [stateOption, setStateOption] = useState([]); 18 | const [cityOption, setCityOption] = useState([]); 19 | 20 | const [state, setState] = useState({ 21 | error: false, 22 | message: '', 23 | heading: '', 24 | type: 'error', 25 | }); 26 | 27 | const validationError = (message) => { 28 | setState({ error: true, message, heading: 'Validation Error' }); 29 | }; 30 | 31 | useEffect(() => { 32 | if (state.error) { 33 | notification.open({ 34 | message: state.heading, 35 | type: state.type, 36 | description: state.message, 37 | }); 38 | setState({ ...state, error: false }); 39 | } 40 | }, [state.error]); 41 | 42 | const submitData = () => { 43 | if ( 44 | !field.name || 45 | !field.type || 46 | !field.experiance || 47 | !field.description || 48 | !field.street1 || 49 | !field.country || 50 | !field.state || 51 | !field.city || 52 | !field.zipcode 53 | ) { 54 | validationError('Please fill in all required fields'); 55 | return; 56 | } 57 | setLoader(true); 58 | const { 59 | street1, 60 | street2, 61 | type, 62 | city, 63 | country, 64 | state, 65 | name, 66 | description, 67 | experiance, 68 | tag, 69 | zipcode, 70 | } = field; 71 | const cityObj = cityOption.filter((c) => c.id === city)[0]; 72 | const categoryObj = categoryOption.filter((c) => c.id === type)[0]; 73 | const stateObj = stateOption.filter((c) => c.id === state)[0]; 74 | 75 | const data = { 76 | name, 77 | tag: tag.split(','), 78 | experiance, 79 | type, 80 | description, 81 | service_id: name + type + experiance, 82 | address: name + street1 + city + experiance, 83 | customers_served: (+city * 12).toString(), 84 | addressObj: { 85 | id: name + street1 + city + experiance, 86 | street1, 87 | street2, 88 | country, 89 | state, 90 | city, 91 | zipcode, 92 | newCountry: { country: 'India', calling_code: '91' }, 93 | newCity: cityObj, 94 | newState: stateObj, 95 | }, 96 | typeObj: categoryObj, 97 | }; 98 | 99 | axios 100 | .post(API.addService, data) 101 | .then((res) => { 102 | if (res.data.success) { 103 | setState({ 104 | error: true, 105 | message: 'Service/Shop added successfully!!', 106 | heading: 'Success', 107 | type: 'success', 108 | }); 109 | navigate( 110 | '/home-services/allServices?category=all&state=all&city=&name=', 111 | ); 112 | } else { 113 | setState({ 114 | error: true, 115 | message: 'Server Error !!', 116 | heading: 'Oops', 117 | type: 'error', 118 | }); 119 | } 120 | setLoader(false); 121 | }) 122 | .catch((e) => { 123 | setState({ 124 | error: true, 125 | message: 'Server Error !!', 126 | heading: 'Oops', 127 | type: 'error', 128 | }); 129 | console.log(e); 130 | setLoader(false); 131 | }); 132 | }; 133 | 134 | const setFieldFn = (name, value) => { 135 | setField({ ...field, [name]: value }); 136 | }; 137 | 138 | useEffect(() => { 139 | axios 140 | .get(API.categories) 141 | .then((res) => { 142 | if (res.data.success) { 143 | setCategoryOption(res.data.data); 144 | } 145 | }) 146 | .catch((e) => console.log(e)); 147 | axios 148 | .get(API.getAllCity) 149 | .then((res) => { 150 | if (res.data.success) { 151 | setCityOption(res.data.data); 152 | } 153 | }) 154 | .catch((e) => console.log(e)); 155 | axios 156 | .get(API.states) 157 | .then((res) => { 158 | if (res.data.success) { 159 | setStateOption(res.data.data); 160 | } 161 | }) 162 | .catch((e) => console.log(e)); 163 | }, []); 164 | 165 | return ( 166 |
167 |
168 | 169 | 170 | 171 | 172 |
173 |
174 | Service/Shop Name: * 175 |
176 | setFieldFn('name', e.target.value)} 179 | placeholder='Service/Shop Name' 180 | /> 181 |
182 | 183 | 184 |
185 |
186 | Category/Type: * 187 |
188 | 198 |
199 | 200 |
201 | 202 | 203 |
204 |
205 | Total experience: * 206 |
207 | setFieldFn('experiance', e.target.value)} 210 | placeholder='1 year 6 months' 211 | /> 212 |
213 | 214 | 215 |
216 |
217 | Tags: 218 |
219 | setFieldFn('tag', e.target.value)} 222 | placeholder='Cheap, Awesome' 223 | /> 224 |
225 | 226 |
227 | 228 | 229 |
230 |
231 | Description: * 232 |
233 | setFieldFn('description', e.target.value)} 236 | placeholder='Enter Description' 237 | /> 238 |
239 | 240 |
241 |
242 | 243 | 244 | 245 |
246 |
247 | Address Line 1: * 248 |
249 | setFieldFn('street1', e.target.value)} 252 | placeholder='Address Line 1' 253 | /> 254 |
255 | 256 | 257 |
258 |
259 | Address Line 2: 260 |
261 | setFieldFn('street2', e.target.value)} 264 | placeholder='Address Line 2' 265 | /> 266 |
267 | 268 |
269 | 270 | 271 |
272 |
273 | Country: * 274 |
275 | 283 |
284 | 285 | 286 |
287 |
288 | State: * 289 |
290 | 301 |
302 | 303 |
304 | 305 | 306 |
307 |
308 | City: * 309 |
310 | 321 |
322 | 323 | 324 |
325 |
326 | Zip code: 327 |
328 | setFieldFn('zipcode', e.target.value)} 331 | placeholder='Zip code' 332 | /> 333 |
334 | 335 |
336 |
337 |
338 |
339 | 345 | 353 |
354 |
355 |
356 | ); 357 | } 358 | -------------------------------------------------------------------------------- /client/src/pages/AllCategory/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Card } from 'antd'; 3 | import { useNavigate } from 'react-router-dom'; 4 | import axios from 'axios'; 5 | import API from '../../api'; 6 | import Spinner from '../Spinner'; 7 | 8 | const { Meta } = Card; 9 | 10 | function Cards() { 11 | const [cards, setCards] = useState([]); 12 | const navigate = useNavigate(); 13 | const [loading, setLoading] = useState(false); 14 | 15 | useEffect(() => { 16 | setLoading(true); 17 | axios 18 | .get(API.categoryDashboard) 19 | .then((res) => { 20 | if (res.data.success) { 21 | setCards(res.data.data); 22 | setLoading(false); 23 | } 24 | }) 25 | .catch((e) => { 26 | console.log(e); 27 | setLoading(false); 28 | }); 29 | }, []); 30 | 31 | return ( 32 | <> 33 |

All Categories

34 | 35 | {!loading ? ( 36 |
37 | {cards.map((card) => ( 38 | { 42 | navigate(`/home-services/allServices?category=${card.id}`); 43 | }} 44 | hoverable 45 | cover={ 46 | {card.name} { 51 | e.target.src = '../../images/default-placeholder.png'; 52 | }} 53 | /> 54 | } 55 | > 56 | 60 | 61 | ))} 62 |
63 | ) : ( 64 | 65 | )} 66 | 67 | ); 68 | } 69 | 70 | export default Cards; 71 | -------------------------------------------------------------------------------- /client/src/pages/AllService/columns.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Tag } from 'antd'; 3 | // import tagColor from "../../config/consts"; 4 | import { Link } from 'react-router-dom'; 5 | 6 | const tagColor = [ 7 | '#ffa39e', 8 | '#ffbb96', 9 | '#ffd591', 10 | '#ffe58f', 11 | '#fffb8f', 12 | '#eaff8f', 13 | '#b7eb8f', 14 | '#87e8de', 15 | '#91d5ff', 16 | '#adc6ff', 17 | '#d3adf7', 18 | '#ffadd2', 19 | ]; 20 | const columns = [ 21 | { 22 | title: 'Name', 23 | dataIndex: 'name', 24 | render: (text, row) => ( 25 | 26 | {text} 27 | 28 | ), 29 | }, 30 | { 31 | title: 'Category', 32 | dataIndex: ['typeObj', 'name'], 33 | }, 34 | { 35 | title: 'CIty', 36 | dataIndex: ['addressObj', 'newCity', 'city'], 37 | }, 38 | { 39 | title: 'State', 40 | dataIndex: ['addressObj', 'newState', 'state'], 41 | }, 42 | { 43 | title: 'Tags', 44 | dataIndex: 'tag', 45 | render: (text) => 46 | text.map((prop) => { 47 | const num = prop.charCodeAt(0) + prop.charCodeAt(prop.length - 1); 48 | return ( 49 | 50 | {prop} 51 | 52 | ); 53 | }), 54 | }, 55 | { 56 | title: 'Owner name', 57 | dataIndex: ['ownerObj', 'name'], 58 | render: (data) => data || 'Not Specified', 59 | }, 60 | { 61 | title: 'Owner number', 62 | dataIndex: ['ownerObj', 'phone'], 63 | render: (data) => data || 'Not Specified', 64 | }, 65 | 66 | { 67 | title: 'Experience', 68 | dataIndex: 'experiance', 69 | }, 70 | { 71 | title: 'Customers Served', 72 | dataIndex: 'customers_served', 73 | }, 74 | ]; 75 | 76 | export default columns; 77 | -------------------------------------------------------------------------------- /client/src/pages/AllService/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Table } from 'antd'; 3 | import { useLocation } from 'react-router-dom'; 4 | import axios from 'axios'; 5 | import columns from './columns'; 6 | import API from '../../api'; 7 | import Spinner from '../Spinner'; 8 | 9 | import { getArrayParams } from '../../utils/paramsConvert'; 10 | 11 | function Services() { 12 | const [datas, setData] = useState([]); 13 | const [loading, setLoading] = useState(false); 14 | const location = useLocation(); 15 | const { search } = location; 16 | 17 | useEffect(() => { 18 | setLoading(true); 19 | axios 20 | .post(API.services, getArrayParams(search)) 21 | .then((res) => { 22 | if (res.data.success) { 23 | setData(res.data.data); 24 | setLoading(false); 25 | } 26 | }) 27 | .catch((e) => { 28 | setLoading(false); 29 | }); 30 | }, [search]); 31 | 32 | return !loading ? ( 33 | render._id} 38 | dataSource={datas} 39 | pagination={{ pageSize: 20 }} 40 | scroll={{ x: 1300 }} 41 | /> 42 | ) : ( 43 | 44 | ); 45 | } 46 | 47 | export default Services; 48 | -------------------------------------------------------------------------------- /client/src/pages/CategoryChart/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { PieChart, Pie, Cell, Tooltip, Legend } from 'recharts'; 3 | import { Spin } from 'antd'; 4 | import axios from 'axios'; 5 | import { useNavigate } from 'react-router-dom'; 6 | import { CHART_COLORS } from '../../config/consts'; 7 | import API from '../../api'; 8 | 9 | const ChartState = () => { 10 | const [collegeData, setCollegeData] = useState([]); 11 | const [loading, setLoading] = useState(false); 12 | const navigate = useNavigate(); 13 | 14 | useEffect(() => { 15 | setLoading(true); 16 | axios.get(API.categoryDashboard).then((res) => { 17 | if (res.data.success) { 18 | setCollegeData( 19 | res.data.data.map((data) => ({ 20 | name: data.name, 21 | count: data.count, 22 | id: data.id, 23 | })), 24 | ); 25 | setLoading(false); 26 | } 27 | }); 28 | }, []); 29 | 30 | return ( 31 |
32 |
33 | {loading ? ( 34 | 35 | ) : ( 36 | 37 | `${(percent * 100).toFixed(0)}%`} 46 | > 47 | {collegeData.map((entry, index) => ( 48 | 53 | navigate( 54 | `/home-services/allServices?category=${entry.id}&state=all&city=&name=`, 55 | ) 56 | } 57 | /> 58 | ))} 59 | 60 | 61 | 67 | 68 | )} 69 |
70 |
71 | ); 72 | }; 73 | 74 | export default ChartState; 75 | -------------------------------------------------------------------------------- /client/src/pages/ChangePassword/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Button, Input, Form, notification } from 'antd'; 3 | import { useNavigate, useParams } from 'react-router-dom'; 4 | import { SendOutlined } from '@ant-design/icons'; 5 | import axios from 'axios'; 6 | import Logo from '../../Layout/Logo'; 7 | import API from '../../api'; 8 | 9 | function ChangePassword() { 10 | const [loading, setLoading] = useState(false); 11 | const navigate = useNavigate(); 12 | const params = useParams(); 13 | 14 | const [form] = Form.useForm(); 15 | const sendOtp = () => { 16 | setLoading(true); 17 | axios 18 | .post(API.changePassword, { 19 | email: params.email, 20 | ...form.getFieldsValue(), 21 | }) 22 | .then((res) => { 23 | if (res.data.success) { 24 | notification.open({ 25 | message: res.data.message, 26 | type: 'success', 27 | }); 28 | 29 | setLoading(false); 30 | navigate(`/`); 31 | } else { 32 | notification.open({ 33 | message: res.data.message, 34 | type: 'error', 35 | }); 36 | } 37 | }) 38 | .catch((e) => { 39 | if (e.response) { 40 | notification.open({ 41 | message: e.response.data.msg, 42 | type: 'error', 43 | }); 44 | } 45 | setLoading(false); 46 | }); 47 | }; 48 | return ( 49 |
50 |
51 |
59 |

HomeServices

60 |
61 |

Welcome back

62 |

Enter new password for

63 |
64 |
65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 88 | 89 | 90 |
91 |
92 |
93 |
94 | ); 95 | } 96 | 97 | export default ChangePassword; 98 | -------------------------------------------------------------------------------- /client/src/pages/ForgotPassword/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Button, Input, Form, notification } from 'antd'; 3 | import { useNavigate ,Link} from 'react-router-dom'; 4 | import { SendOutlined } from '@ant-design/icons'; 5 | import axios from 'axios'; 6 | import Logo from '../../Layout/Logo'; 7 | import API from '../../api'; 8 | import PublicLayout from '../../Layout/PublicLayout'; 9 | 10 | function ForgotPassword() { 11 | const [loading, setLoading] = useState(false); 12 | const navigate = useNavigate(); 13 | 14 | const [form] = Form.useForm(); 15 | const sendOtp = () => { 16 | setLoading(true); 17 | axios 18 | .post(API.sendOtp, form.getFieldsValue()) 19 | .then((res) => { 20 | if (res.data.success) { 21 | notification.open({ 22 | message: res.data.message, 23 | type: 'success', 24 | }); 25 | 26 | setLoading(false); 27 | navigate('/verifyOtp'); 28 | } else { 29 | notification.open({ 30 | message: res.data.message, 31 | type: 'error', 32 | }); 33 | } 34 | }) 35 | .catch((e) => { 36 | if (e.response) { 37 | notification.open({ 38 | message: e.response.data.msg, 39 | type: 'error', 40 | }); 41 | } 42 | setLoading(false); 43 | }); 44 | }; 45 | return ( 46 | 47 |
48 |
49 | 50 |
51 |

Welcome back

52 |

53 | Enter your email to recover your account 54 |

55 |
56 |
57 | 58 | 59 | 60 | 61 | 62 | 77 | 78 | 79 |
80 | Back 81 |
82 |
83 | 84 | ); 85 | } 86 | 87 | export default ForgotPassword; 88 | -------------------------------------------------------------------------------- /client/src/pages/Login/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useContext } from 'react'; 2 | import { Button, Input, Form, Divider, notification, Checkbox } from 'antd'; 3 | import { Link, useNavigate } from 'react-router-dom'; 4 | import { 5 | EyeInvisibleOutlined, 6 | EyeTwoTone, 7 | UserOutlined, 8 | LockOutlined, 9 | GoogleOutlined, 10 | FacebookOutlined, 11 | } from '@ant-design/icons'; 12 | import axios from 'axios'; 13 | import Logo from '../../Layout/Logo'; 14 | import API from '../../api'; 15 | import { getauth, setauth } from '../../utils/auth'; 16 | import { GlobalContext } from '../../Context/GlobalContext'; 17 | import PublicLayout from '../../Layout/PublicLayout'; 18 | 19 | function SignIn(props) { 20 | const [form] = Form.useForm(); 21 | const { data, dispatch } = useContext(GlobalContext); 22 | const [state, setState] = useState({ loader: false, error: false }); 23 | const navigate = useNavigate(); 24 | 25 | useEffect(() => { 26 | const token = getauth(); 27 | axios.get(API.auth, { headers: { 'auth-token': token } }).then((res) => { 28 | if (res.data.success === true) { 29 | dispatch({ type: 'LOGIN_SUCCESS' }); 30 | let { isMerchant } = res.data.data; 31 | if (!isMerchant) isMerchant = false; 32 | 33 | dispatch({ type: 'SET_MERCHANT', isMerchant }); 34 | let path = 35 | '/home-services/allCategories?category=all&state=all&city=&name='; 36 | if (props.location && props.location.state && props.location.state.data) 37 | path = 38 | props.location.state.data.pathname + 39 | props.location.state.data.search; 40 | navigate(path); 41 | } 42 | }); 43 | }, []); 44 | 45 | const login = () => { 46 | setState({ ...state, loader: true }); 47 | const datas = form.getFieldsValue(); 48 | axios 49 | .post(API.login, datas) 50 | .then((res) => { 51 | if (res.status) { 52 | dispatch({ type: 'LOGIN_SUCCESS' }); 53 | let { isMerchant } = res.data.data; 54 | if (!isMerchant) isMerchant = false; 55 | 56 | dispatch({ type: 'SET_MERCHANT', isMerchant }); 57 | 58 | if (data.categories.length === 0) 59 | axios 60 | .get(API.categories) 61 | .then((res) => { 62 | if (res.data.success) { 63 | dispatch({ type: 'SET_CATEGORIES', payload: res.data.data }); 64 | } 65 | }) 66 | .catch((e) => console.log(e)); 67 | if (data.states.length === 0) 68 | axios 69 | .get(API.states) 70 | .then((res) => { 71 | if (res.data.success) { 72 | dispatch({ type: 'SET_STATES', payload: res.data.data }); 73 | } 74 | }) 75 | .catch((e) => console.log(e)); 76 | if (res.headers.auth) { 77 | setauth(res.headers.auth); 78 | } 79 | setState({ ...state, loader: false }); 80 | navigate( 81 | '/home-services/allCategories?category=all&state=all&city=&name=', 82 | ); 83 | } else { 84 | setState({ error: true, loader: false }); 85 | } 86 | }) 87 | .catch((e) => { 88 | setState({ error: true, loader: false }); 89 | }); 90 | }; 91 | useEffect(() => { 92 | if (state.error) { 93 | notification.open({ 94 | message: 'Wrong Credentials', 95 | type: 'error', 96 | }); 97 | setState({ ...state, error: false }); 98 | } 99 | }, [state.error]); 100 | 101 | return ( 102 | 103 |
104 |
105 | 106 |
107 |

108 | Welcome back to HomeServices 109 |

110 |

Please log into your account

111 |
117 | 126 | } placeholder='Email' size='large' /> 127 | 128 | 137 | } 139 | placeholder='Password' 140 | size='large' 141 | iconRender={(visible) => 142 | visible ? : 143 | } 144 | /> 145 | 146 |
147 | 148 | Remember me 149 | 150 | 151 | Forgot Password? 152 | 153 |
154 | 164 | 165 | Or login with 166 |
167 | 170 | 173 |
174 |

175 | Don't Have an Account?{' '} 176 | 177 | Register now 178 | 179 |

180 |
181 |
182 | ); 183 | } 184 | 185 | export default SignIn; 186 | -------------------------------------------------------------------------------- /client/src/pages/Registration/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Button, Input, Form, Divider, notification, Checkbox } from 'antd'; 3 | import { Link, useNavigate } from 'react-router-dom'; 4 | import { 5 | EyeInvisibleOutlined, 6 | EyeTwoTone, 7 | GoogleOutlined, 8 | FacebookOutlined, 9 | } from '@ant-design/icons'; 10 | import axios from 'axios'; 11 | 12 | import Logo from '../../Layout/Logo'; 13 | import API from '../../api'; 14 | import PublicLayout from '../../Layout/PublicLayout'; 15 | 16 | function SignUp() { 17 | const [form] = Form.useForm(); 18 | const [isMerchant, setIsMerchant] = useState(false); 19 | const [state, setState] = useState({ 20 | loader: false, 21 | error: false, 22 | message: 'Please provide valid details', 23 | type: 'error', 24 | }); 25 | const navigate = useNavigate(); 26 | 27 | const registration = () => { 28 | setState({ ...state, loader: true }); 29 | const data = form.getFieldsValue(); 30 | data.isMerchant = isMerchant; 31 | axios 32 | .post(API.registration, data) 33 | .then((res) => { 34 | if (res.status) { 35 | setState({ 36 | ...state, 37 | loader: false, 38 | message: 'Registration Success', 39 | type: 'success', 40 | error: true, 41 | }); 42 | navigate('/login'); 43 | } else { 44 | setState({ ...state, error: true, loader: false }); 45 | } 46 | }) 47 | .catch((e) => { 48 | setState({ ...state, error: true, loader: false }); 49 | }); 50 | }; 51 | 52 | useEffect(() => { 53 | if (state.error) { 54 | notification.open({ 55 | message: state.message, 56 | type: state.type, 57 | }); 58 | setState({ ...state, error: false }); 59 | } 60 | }, [state.error, state.message, state.type]); 61 | 62 | return ( 63 | 64 |
65 |
66 | 67 |
68 |

Welcome to HomeServices

69 |

Please register your account

70 |
71 | 81 | 82 | 83 | 88 | 89 | 90 | 100 | 103 | visible ? : 104 | } 105 | /> 106 | 107 | ({ 117 | validator(_, value) { 118 | if (!value || getFieldValue('password') === value) { 119 | return Promise.resolve(); 120 | } 121 | return Promise.reject( 122 | new Error('The two passwords do not match'), 123 | ); 124 | }, 125 | }), 126 | ]} 127 | > 128 | 131 | visible ? : 132 | } 133 | /> 134 | 135 |
136 |
137 | { 139 | setIsMerchant(e.target.checked); 140 | }} 141 | > 142 | Merchant/Owner/Worker 143 | 144 |
145 |
146 | 157 | 158 | Or Register Up With 159 |
160 | 163 | 166 |
167 |

168 | Already Have an Account?{' '} 169 | 170 | Login 171 | 172 |

173 |
174 |
175 | ); 176 | } 177 | 178 | export default SignUp; 179 | -------------------------------------------------------------------------------- /client/src/pages/ServiceChart/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { PieChart, Pie, Cell, Tooltip, Legend } from 'recharts'; 3 | import { Spin } from 'antd'; 4 | import axios from 'axios'; 5 | import { useNavigate } from 'react-router-dom'; 6 | import { CHART_COLORS } from '../../config/consts'; 7 | import API from '../../api'; 8 | 9 | const ChartState = () => { 10 | const [collegeData, setCollegeData] = useState([]); 11 | const [loading, setLoading] = useState(false); 12 | const navigate = useNavigate(); 13 | 14 | useEffect(() => { 15 | setLoading(true); 16 | axios.get(API.servicesChart).then((res) => { 17 | if (res.data.success) { 18 | setCollegeData( 19 | res.data.data.map((data) => ({ 20 | name: data.name, 21 | count: data.count, 22 | id: data.id, 23 | })), 24 | ); 25 | setLoading(false); 26 | } 27 | }); 28 | }, []); 29 | 30 | return ( 31 |
32 |
33 | {loading ? ( 34 | 35 | ) : ( 36 | 37 | `${(percent * 100).toFixed(0)}%`} 46 | > 47 | {collegeData.map((entry, index) => ( 48 | 53 | navigate( 54 | `/home-services/allServices?category=all&state=all&city=${entry.name}&name=`, 55 | ) 56 | } 57 | /> 58 | ))} 59 | 60 | 61 | 67 | 68 | )} 69 |
70 |
71 | ); 72 | }; 73 | 74 | export default ChartState; 75 | -------------------------------------------------------------------------------- /client/src/pages/ServiceDetail/Address.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Card } from 'antd'; 3 | 4 | function Address({ data }) { 5 | return ( 6 | 7 |
8 |
9 | Street 1: 10 | {data?.addressObj.street1} 11 |
12 |
13 | Street 2: 14 | {data?.addressObj.street2} 15 |
16 |
17 | City: 18 | {data?.addressObj.newCity?.city} 19 |
20 |
21 | State: 22 | {data?.addressObj.newState?.state} 23 |
24 |
25 | Country: 26 | {data?.addressObj.newCountry?.country} 27 |
28 |
29 | ZipCode: 30 | {data?.addressObj?.zipcode} 31 |
32 |
33 |
34 | ); 35 | } 36 | 37 | export default Address; 38 | -------------------------------------------------------------------------------- /client/src/pages/ServiceDetail/MainDetails.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Card, Tag } from 'antd'; 3 | import tagColor from '../../config/consts'; 4 | 5 | function MainDetails({ data }) { 6 | return ( 7 | <> 8 | {data && ( 9 | 10 |
11 |
12 |
Name:
13 |
{data.name}
14 |
15 |
16 |
Description:
17 |
{data.description}
18 |
19 |
20 |
Category/Type:
21 |
{data.typeObj?.name}
22 |
23 |
24 |
Tags:
25 |
26 | {data?.tag?.map((prop) => { 27 | const num = 28 | prop.charCodeAt(0) + prop.charCodeAt(prop.length - 1); 29 | return ( 30 | 31 | {prop} 32 | 33 | ); 34 | })} 35 |
36 |
37 |
38 |
Experience:
39 |
{data.experience}
40 |
41 |
42 |
43 | )} 44 | 45 | ); 46 | } 47 | 48 | export default MainDetails; 49 | -------------------------------------------------------------------------------- /client/src/pages/ServiceDetail/OtherInfo.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Card, Row } from 'antd'; 3 | 4 | function OtherInfo({ data }) { 5 | return ( 6 | 7 | 8 | Experience: 9 | {data?.experiance} 10 | 11 | 12 | Total Customer Served: 13 | {data?.customers_served} 14 | 15 | 16 | Rating: 17 | ⭐️ ⭐️ ⭐️ ⭐️ 18 | 19 | 20 | 21 | Feedback: 22 | Best in area 23 | 24 | 25 | ); 26 | } 27 | 28 | export default OtherInfo; 29 | -------------------------------------------------------------------------------- /client/src/pages/ServiceDetail/OwnerInfo.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Card, Row } from 'antd'; 3 | 4 | function OwnerInfo({ data }) { 5 | return ( 6 | 7 | 8 | Name: 9 | {data?.ownerObj?.name} 10 | 11 | 12 | Email: 13 | {data?.ownerObj?.email} 14 | 15 | 16 | Phone: 17 | {data?.ownerObj?.phone} 18 | 19 | 20 | Gender: 21 | {data?.ownerObj?.gender} 22 | 23 | 24 | Address: 25 | 26 | {data?.ownerObj?.addressObj?.street1},{' '} 27 | {data?.ownerObj?.addressObj?.street2},{' '} 28 | {data?.ownerObj?.addressObj?.newCity?.city},{' '} 29 | {data?.ownerObj?.addressObj?.newState?.state},{' '} 30 | {data?.ownerObj?.addressObj?.newCountry?.country} 31 | 32 | 33 | 34 | ); 35 | } 36 | 37 | export default OwnerInfo; 38 | -------------------------------------------------------------------------------- /client/src/pages/ServiceDetail/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { useParams } from 'react-router-dom'; 3 | import { Tabs } from 'antd'; 4 | import { AppleOutlined, AndroidOutlined } from '@ant-design/icons'; 5 | import axios from 'axios'; 6 | import Spinner from '../Spinner'; 7 | import API from '../../api'; 8 | import MainDetails from './MainDetails'; 9 | import OtherInfo from './OtherInfo'; 10 | import Address from './Address'; 11 | import OwnerInfo from './OwnerInfo'; 12 | 13 | const { TabPane } = Tabs; 14 | 15 | function ServiceDetail() { 16 | const [collegeData, setCollegeData] = useState(null); 17 | const [collegeLoading, setCollegeLoading] = useState(false); 18 | const { serviceId } = useParams(); 19 | 20 | useEffect(() => { 21 | setCollegeLoading(true); 22 | axios 23 | .get(API.getServiceById.replace(':serviceId', serviceId)) 24 | .then((response) => { 25 | if (response.data.success) { 26 | setCollegeData(response.data.data[0]); 27 | setCollegeLoading(false); 28 | } 29 | }) 30 | .catch((error) => { 31 | console.error('Error fetching college data:', error); 32 | setCollegeLoading(false); 33 | }); 34 | }, [serviceId]); 35 | 36 | return ( 37 |
38 | {collegeLoading ? ( 39 | 40 | ) : ( 41 | 42 | 45 | 46 | Service Info 47 | 48 | } 49 | key='1' 50 | > 51 | 52 | 53 | 56 | 57 | Address 58 | 59 | } 60 | key='2' 61 | > 62 |
63 | 64 | 67 | 68 | Other Information 69 | 70 | } 71 | key='3' 72 | > 73 | 74 | 75 | 78 | 79 | Owner Information 80 | 81 | } 82 | key='4' 83 | > 84 | 85 | 86 | 87 | )} 88 |
89 | ); 90 | } 91 | 92 | export default ServiceDetail; 93 | -------------------------------------------------------------------------------- /client/src/pages/Spinner/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './spinner.module.css'; 4 | 5 | function Spinner() { 6 | return ( 7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | ); 20 | } 21 | 22 | export default Spinner; 23 | -------------------------------------------------------------------------------- /client/src/pages/Spinner/spinner.module.css: -------------------------------------------------------------------------------- 1 | .spinner { 2 | position: absolute; 3 | top: 0; 4 | left: 0; 5 | height: 100%; 6 | width: 100%; 7 | background: rgba(1, 1, 1, 0.6); 8 | display: flex; 9 | justify-content: center; 10 | align-items: center; 11 | z-index: 5; 12 | } 13 | 14 | .lds_roller { 15 | display: inline-block; 16 | position: relative; 17 | width: 80px; 18 | height: 80px; 19 | } 20 | .lds_roller div { 21 | animation: lds_roller 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; 22 | transform-origin: 40px 40px; 23 | } 24 | .lds_roller div:after { 25 | content: " "; 26 | display: block; 27 | position: absolute; 28 | width: 7px; 29 | height: 7px; 30 | border-radius: 50%; 31 | background: #fff; 32 | margin: -4px 0 0 -4px; 33 | } 34 | .lds_roller div:nth-child(1) { 35 | animation-delay: -0.036s; 36 | } 37 | .lds_roller div:nth-child(1):after { 38 | top: 63px; 39 | left: 63px; 40 | } 41 | .lds_roller div:nth-child(2) { 42 | animation-delay: -0.072s; 43 | } 44 | .lds_roller div:nth-child(2):after { 45 | top: 68px; 46 | left: 56px; 47 | } 48 | .lds_roller div:nth-child(3) { 49 | animation-delay: -0.108s; 50 | } 51 | .lds_roller div:nth-child(3):after { 52 | top: 71px; 53 | left: 48px; 54 | } 55 | .lds_roller div:nth-child(4) { 56 | animation-delay: -0.144s; 57 | } 58 | .lds_roller div:nth-child(4):after { 59 | top: 72px; 60 | left: 40px; 61 | } 62 | .lds_roller div:nth-child(5) { 63 | animation-delay: -0.18s; 64 | } 65 | .lds_roller div:nth-child(5):after { 66 | top: 71px; 67 | left: 32px; 68 | } 69 | .lds_roller div:nth-child(6) { 70 | animation-delay: -0.216s; 71 | } 72 | .lds_roller div:nth-child(6):after { 73 | top: 68px; 74 | left: 24px; 75 | } 76 | .lds_roller div:nth-child(7) { 77 | animation-delay: -0.252s; 78 | } 79 | .lds_roller div:nth-child(7):after { 80 | top: 63px; 81 | left: 17px; 82 | } 83 | .lds_roller div:nth-child(8) { 84 | animation-delay: -0.288s; 85 | } 86 | .lds_roller div:nth-child(8):after { 87 | top: 56px; 88 | left: 12px; 89 | } 90 | @keyframes lds_roller { 91 | 0% { 92 | transform: rotate(0deg); 93 | } 94 | 100% { 95 | transform: rotate(360deg); 96 | } 97 | } -------------------------------------------------------------------------------- /client/src/pages/VerifyOtp/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Button, Input, Form, notification } from 'antd'; 3 | import { Link, useNavigate } from 'react-router-dom'; 4 | import { UnlockOutlined } from '@ant-design/icons'; 5 | import axios from 'axios'; 6 | import Logo from '../../Layout/Logo'; 7 | import API from '../../api'; 8 | import PublicLayout from '../../Layout/PublicLayout'; 9 | 10 | function SignIn() { 11 | const [loading, setLoading] = useState(false); 12 | const navigate = useNavigate(); 13 | const [counter, setCounter] = React.useState(60); 14 | 15 | const [form] = Form.useForm(); 16 | const sendOtp = () => { 17 | setLoading(true); 18 | axios 19 | .post(API.verifyOtp, form.getFieldsValue()) 20 | .then((res) => { 21 | if (res.data.success) { 22 | notification.open({ 23 | message: res.data.message, 24 | type: 'success', 25 | }); 26 | 27 | setLoading(false); 28 | navigate(`/changePassword/${res.data.data.email}`); 29 | } else { 30 | notification.open({ 31 | message: res.data.message, 32 | type: 'error', 33 | }); 34 | } 35 | }) 36 | .catch((e) => { 37 | if (e.response) { 38 | notification.open({ 39 | message: e.response.data.msg, 40 | type: 'error', 41 | }); 42 | } 43 | setLoading(false); 44 | }); 45 | }; 46 | React.useEffect(() => { 47 | if (counter > 0) { 48 | setTimeout(() => setCounter(counter - 1), 1000); 49 | } 50 | }, [counter]); 51 | return ( 52 | 53 |
54 |
55 | 56 |
57 |

Welcome back

58 |

59 | Enter your otp to change your password 60 |

61 |
62 |
63 | 69 | 70 | 71 | 72 | 73 | 89 | {counter < 2 ? ( 90 | Don't get otp? Resend It 91 | ) : ( 92 | `Resend after ${counter} second` 93 | )} 94 | 95 | 96 |
97 |
98 |
99 | ); 100 | } 101 | 102 | export default SignIn; 103 | -------------------------------------------------------------------------------- /client/src/routes.jsx: -------------------------------------------------------------------------------- 1 | import React, { lazy, Suspense, useContext } from 'react'; 2 | import { 3 | Route, 4 | Routes, 5 | BrowserRouter as Router, 6 | Navigate, 7 | Outlet, 8 | } from 'react-router-dom'; 9 | import Spinner from './pages/Spinner'; 10 | import { GlobalContext } from './Context/GlobalContext'; 11 | import { getauth } from './utils/auth'; 12 | import Layout from './Layout'; 13 | 14 | const routes = [ 15 | { 16 | path: '/', 17 | exact: true, 18 | Component: lazy(() => import('./pages/AllCategory')), 19 | }, 20 | { 21 | path: 'allCategories', 22 | exact: true, 23 | Component: lazy(() => import('./pages/AllCategory')), 24 | }, 25 | { 26 | path: 'addCategory', 27 | exact: true, 28 | Component: lazy(() => import('./pages/AddCategory')), 29 | }, 30 | { 31 | path: 'allServices', 32 | exact: true, 33 | Component: lazy(() => import('./pages/AllService')), 34 | }, 35 | { 36 | path: 'addService', 37 | exact: true, 38 | Component: lazy(() => import('./pages/AddService')), 39 | }, 40 | { 41 | path: 'allServices/:serviceId', 42 | exact: true, 43 | Component: lazy(() => import('./pages/ServiceDetail')), 44 | }, 45 | { 46 | path: 'charts/services', 47 | exact: true, 48 | Component: lazy(() => import('./pages/ServiceChart')), 49 | }, 50 | { 51 | path: 'charts/categories', 52 | exact: true, 53 | Component: lazy(() => import('./pages/CategoryChart')), 54 | }, 55 | ]; 56 | 57 | const PrivateRoutes = ({ isLoggedIn }) => 58 | isLoggedIn ? : ; 59 | 60 | const PublicRoutes = () => { 61 | const { data } = useContext(GlobalContext); 62 | 63 | const auth = getauth(); 64 | 65 | const isLoggedIn = data.isLoggedIn || !!auth; 66 | 67 | return ( 68 | 69 | }> 70 | 71 | import('./pages/Login'))} /> 72 | import('./pages/Login'))} 75 | /> 76 | import('./pages/Registration'))} 79 | /> 80 | import('./pages/VerifyOtp'))} 83 | /> 84 | import('./pages/ChangePassword'))} 87 | /> 88 | import('./pages/ForgotPassword'))} 91 | /> 92 | }> 93 | {routes.map(({ path, Component, exact }) => ( 94 | 100 | 101 | 102 | } 103 | > 104 | ))} 105 | 106 | 107 | 108 | 109 | ); 110 | }; 111 | 112 | export default PublicRoutes; 113 | -------------------------------------------------------------------------------- /client/src/utils/auth.js: -------------------------------------------------------------------------------- 1 | export const setauth = (token) => { 2 | const auth = token.slice(0, Math.floor(token.length / 2)); 3 | const user = token.slice(Math.floor(token.length / 2), token.length); 4 | 5 | window.localStorage.setItem('auth-token', auth); 6 | window.localStorage.setItem('user_id', user); 7 | }; 8 | 9 | export const getauth = () => { 10 | const auth = window.localStorage.getItem('auth-token'); 11 | const user = window.localStorage.getItem('user_id'); 12 | if (auth && user) return auth + user; 13 | return null; 14 | }; 15 | 16 | export const clearToken = () => { 17 | localStorage.removeItem('auth-token'); 18 | localStorage.removeItem('user_id'); 19 | }; 20 | -------------------------------------------------------------------------------- /client/src/utils/paramsConvert.js: -------------------------------------------------------------------------------- 1 | export const getArrayParams = (urlString) => { 2 | let final = {}; 3 | urlString 4 | .slice(1) 5 | .split('&') 6 | ?.forEach((data) => { 7 | final = { ...final, [data.split('=')[0]]: data.split('=')[1] }; 8 | }); 9 | return final; 10 | }; 11 | 12 | export const setUrlString = (obj) => { 13 | let category = 'all'; 14 | let city = ''; 15 | let state = 'all'; 16 | let name = ''; 17 | if (obj.category !== undefined) { 18 | category = obj.category; 19 | } 20 | if (obj.state !== undefined) { 21 | state = obj.state; 22 | } 23 | if (obj.city !== undefined) { 24 | city = obj.city; 25 | } 26 | if (obj.name !== undefined) { 27 | name = obj.name; 28 | } 29 | return `?category=${category}&state=${state}&city=${city}&name=${name}`; 30 | }; 31 | -------------------------------------------------------------------------------- /client/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | /** @type {import('tailwindcss').Config} */ 3 | 4 | // Custom color with css variable color in __theme_color.scss 5 | function customColors(cssVar) { 6 | return ({ opacityVariable, opacityValue }) => { 7 | if (opacityValue !== undefined) { 8 | return `rgba(var(${cssVar}), ${opacityValue})`; 9 | } 10 | if (opacityVariable !== undefined) { 11 | return `rgba(var(${cssVar}), var(${opacityVariable}, 1))`; 12 | } 13 | return `rgb(var(${cssVar}))`; 14 | }; 15 | } 16 | 17 | module.exports = { 18 | content: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'], 19 | darkMode: 'class', 20 | theme: { 21 | container: { 22 | center: true, 23 | padding: { 24 | DEFAULT: '1rem', 25 | '2xl': '128px', 26 | }, 27 | }, 28 | 29 | extend: { 30 | colors: { 31 | primary: { 32 | 50: customColors('--c-primary-50'), 33 | 100: customColors('--c-primary-100'), 34 | 200: customColors('--c-primary-200'), 35 | 300: customColors('--c-primary-300'), 36 | 400: customColors('--c-primary-400'), 37 | 500: customColors('--c-primary-500'), 38 | 6000: customColors('--c-primary-600'), 39 | 700: customColors('--c-primary-700'), 40 | 800: customColors('--c-primary-800'), 41 | 900: customColors('--c-primary-900'), 42 | }, 43 | secondary: { 44 | 50: customColors('--c-secondary-50'), 45 | 100: customColors('--c-secondary-100'), 46 | 200: customColors('--c-secondary-200'), 47 | 300: customColors('--c-secondary-300'), 48 | 400: customColors('--c-secondary-400'), 49 | 500: customColors('--c-secondary-500'), 50 | 6000: customColors('--c-secondary-600'), 51 | 700: customColors('--c-secondary-700'), 52 | 800: customColors('--c-secondary-800'), 53 | 900: customColors('--c-secondary-900'), 54 | }, 55 | neutral: { 56 | 50: customColors('--c-neutral-50'), 57 | 100: customColors('--c-neutral-100'), 58 | 200: customColors('--c-neutral-200'), 59 | 300: customColors('--c-neutral-300'), 60 | 400: customColors('--c-neutral-400'), 61 | 500: customColors('--c-neutral-500'), 62 | 6000: customColors('--c-neutral-600'), 63 | 700: customColors('--c-neutral-700'), 64 | 800: customColors('--c-neutral-800'), 65 | 900: customColors('--c-neutral-900'), 66 | }, 67 | }, 68 | }, 69 | }, 70 | variants: { 71 | extend: {}, 72 | }, 73 | plugins: [ 74 | require('@tailwindcss/typography'), 75 | require('@tailwindcss/aspect-ratio'), 76 | ], 77 | }; 78 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | const config = {}; 2 | 3 | const isDevelopment = process.env.NODE_ENV === 'development' ? true : false; 4 | 5 | config.PORT = isDevelopment ? 5000 : process.env.PORT || 5000; 6 | config.API_VERSION = isDevelopment ? '/v1' : process.env.API_VERSION || '/v1'; 7 | config.NODE_ENV = process.env.NODE_ENV; 8 | config.DB_STRING = isDevelopment ? '' : process.env.DB_STRING; 9 | config.SECREATE = isDevelopment ? 'development' : process.env.SECREATE; 10 | 11 | module.exports = { config }; 12 | -------------------------------------------------------------------------------- /consts.js: -------------------------------------------------------------------------------- 1 | const errorMessage = { 2 | NO_RESOURCE: 'No resource found', 3 | }; 4 | const course = { 5 | CE: 'CE', 6 | IT: 'IT', 7 | BE: 'BE', 8 | MECHANICAL: 'mechanical', 9 | ELECTRONICS: 'electronics', 10 | AERONAUTICAL: 'aeronautical', 11 | AUTOMOBILE: 'automobile', 12 | PHARMEASY: 'pharmeasy', 13 | MEDICAL: 'medical', 14 | COMPUTER: 'computer', 15 | ARTS: 'arts', 16 | CHEMICAL: 'chemical', 17 | DIPLOMA: 'diploma', 18 | NETWORK: 'network', 19 | MACHINE_LEARNING: 'machine-learning', 20 | WEBDEVLOPMENT: 'webdevlopment', 21 | BSC: 'Bsc', 22 | MSC: 'Msc', 23 | }; 24 | 25 | module.exports = { errorMessage, course }; 26 | -------------------------------------------------------------------------------- /controller/category.js: -------------------------------------------------------------------------------- 1 | const Categories = require('../schemas/Category'); 2 | 3 | const fs = require('fs'); 4 | let multer = require('multer'); 5 | var storage = multer.diskStorage({ 6 | destination: function (req, file, cb) { 7 | cb(null, `\categoryImages`); 8 | }, 9 | filename: function (req, file, cb) { 10 | cb(null, req.body.name + '.png'); 11 | }, 12 | }); 13 | var upload = multer({ storage: storage }).single('image'); 14 | 15 | const { resourceError, serverError } = require('../helper/errorHandler'); 16 | const { success } = require('../helper/successHandler'); 17 | const NewServices = require('../schemas/NewServices'); 18 | 19 | exports.getCategories = async (req, res) => { 20 | try { 21 | const categories = await Categories.find(); 22 | success(res, categories); 23 | } catch (error) { 24 | serverError(res, error); 25 | } 26 | }; 27 | exports.addCategory = async (req, res) => { 28 | try { 29 | const categories = await Categories.find(); 30 | if ( 31 | categories.filter( 32 | (c) => c.name.toLowerCase() == req.body.name.toLowerCase(), 33 | ).length > 0 34 | ) { 35 | resourceError(res, 'Category Already Exits.'); 36 | } else { 37 | try { 38 | fs.writeFileSync( 39 | `./categoryImages/${req.body.name}.png`, 40 | req.file.buffer, 41 | 'base64', 42 | ); 43 | const Category = new Categories(); 44 | Category.name = req.body.name; 45 | Category.id = req.body.name; 46 | const newCategory = await Category.save(); 47 | success(res, newCategory); 48 | } catch (e) { 49 | serverError(res, e); 50 | } 51 | } 52 | } catch (error) { 53 | serverError(res, error); 54 | } 55 | }; 56 | 57 | exports.categoriesDashboard = async (req, res) => { 58 | try { 59 | const categories = await Categories.find(); 60 | const services = await NewServices.find(); 61 | let countCategories = {}; 62 | let finalCategories = []; 63 | services.map((service) => { 64 | if (countCategories[service.type]) { 65 | countCategories[service.type] = countCategories[service.type] + 1; 66 | } else { 67 | countCategories[service.type] = 1; 68 | } 69 | }); 70 | finalCategories = categories.map((category) => { 71 | return { 72 | id: category.id, 73 | name: category.name, 74 | count: countCategories[category.id] || 0, 75 | }; 76 | }); 77 | success(res, finalCategories); 78 | } catch (error) { 79 | serverError(res, error); 80 | } 81 | }; 82 | -------------------------------------------------------------------------------- /controller/city.js: -------------------------------------------------------------------------------- 1 | const Cities = require('../schemas/City'); 2 | const { resourceError, serverError } = require('../helper/errorHandler'); 3 | const { success } = require('../helper/successHandler'); 4 | 5 | exports.getAllCity = async (req, res) => { 6 | try { 7 | const city = await Cities.find(); 8 | success(res, city); 9 | } catch (e) { 10 | serverError(res, error); 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /controller/college.js: -------------------------------------------------------------------------------- 1 | const College = require('../schemas/College'); 2 | const { resourceError, serverError } = require('../helper/errorHandler'); 3 | const { success } = require('../helper/successHandler'); 4 | const { 5 | errorMessage: { NO_RESOURCE }, 6 | course, 7 | } = require('../consts'); 8 | 9 | exports.getColleges = async (req, res) => { 10 | try { 11 | const colleges = await College.find(); 12 | success(res, colleges); 13 | } catch (error) { 14 | serverError(res, error); 15 | } 16 | }; 17 | 18 | exports.getCollegeById = async (req, res) => { 19 | const { collegeId } = req.params; 20 | if (!collegeId) resourceError(res, 'Please enter college'); 21 | try { 22 | const college = await College.findById(collegeId); 23 | if (college) success(res, college); 24 | else resourceError(res, NO_RESOURCE); 25 | } catch (error) { 26 | serverError(res, error); 27 | } 28 | }; 29 | 30 | exports.getCollegesByState = async (req, res) => { 31 | const { stateId } = req.params; 32 | if (!stateId) resourceError(res, 'Please enter state'); 33 | try { 34 | const regex = new RegExp(stateId, 'i'); 35 | const colleges = await College.find({ state: { $regex: regex } }); 36 | if (colleges) success(res, colleges); 37 | else resourceError(res, NO_RESOURCE); 38 | } catch (error) { 39 | serverError(res, error); 40 | } 41 | }; 42 | 43 | exports.getCollegesByLocation = async (req, res) => { 44 | const { locationId } = req.params; 45 | if (!locationId) resourceError(res, 'Please enter city or location'); 46 | try { 47 | const regex = new RegExp(locationId, 'i'); 48 | const colleges = await College.find({ city: { $regex: regex } }); 49 | if (colleges) success(res, colleges); 50 | else resourceError(res, NO_RESOURCE); 51 | } catch (error) { 52 | serverError(res, error); 53 | } 54 | }; 55 | 56 | exports.getCollegesChart = async (req, res) => { 57 | try { 58 | const colleges = await College.find(); 59 | let counts = {}; 60 | let data = []; 61 | const state = colleges.map((dat) => dat.state); 62 | state?.forEach((el) => (counts[el] = 1 + (counts[el] || 0))); 63 | const keys = Object.keys(counts); 64 | const values = Object.values(counts); 65 | data = keys.map((k, i) => ({ name: k, value: values[i] })); 66 | success(res, data); 67 | } catch (error) { 68 | serverError(res, error); 69 | } 70 | }; 71 | 72 | exports.getCollegesCourseChart = async (req, res) => { 73 | try { 74 | let data = []; 75 | let counts = {}; 76 | const colleges = await College.find(); 77 | Object.keys(course)?.forEach((key) => 78 | colleges?.forEach((college) => { 79 | if (college.courses.includes(course[key])) 80 | counts[course[key]] = 1 + (counts[course[key]] || 0); 81 | }), 82 | ); 83 | const dataKey = Object.keys(counts); 84 | const dataValue = Object.values(counts); 85 | data = dataKey.map((k, i) => ({ name: k, value: dataValue[i] })); 86 | success(res, data); 87 | } catch (error) { 88 | serverError(res, error); 89 | } 90 | }; 91 | -------------------------------------------------------------------------------- /controller/logincontrol.js: -------------------------------------------------------------------------------- 1 | const User = require('../schemas/UserModel'); 2 | const bcrypt = require('bcryptjs'); 3 | const nodemailer = require('nodemailer'); 4 | const jwt = require('jsonwebtoken'); 5 | 6 | const transporter = nodemailer.createTransport({ 7 | service: 'gmail', 8 | host: 'smtp.gmail.com', 9 | port: 465, 10 | secure: true, 11 | auth: { 12 | user: process.env.GMAIL, 13 | pass: process.env.GMAIL_PASSWORD, 14 | }, 15 | }); 16 | 17 | exports.singin = async (req, res, next) => { 18 | if (!req.body.email || !req.body.password) { 19 | return res.status(400).json({ 20 | success: false, 21 | msg: 'email or password not empty', 22 | }); 23 | } 24 | if (req.body.email.trim() === '' || req.body.password.trim() === '') { 25 | return res.status(400).json({ 26 | success: false, 27 | msg: 'email or password not empty', 28 | }); 29 | } 30 | const user = await User.find({ email: req.body.email }); 31 | if (user.length === 0) { 32 | return res.status(400).json({ 33 | success: false, 34 | msg: 'Wrong email or password', 35 | }); 36 | } 37 | bcrypt.compare(req.body.password, user[0].password, (err, isMatch) => { 38 | if (err) { 39 | return res.status(500).json({ 40 | success: false, 41 | msg: 'SERVER ERROR', 42 | }); 43 | } 44 | if (isMatch) { 45 | const token = jwt.sign({ id: user[0]._id }, process.env.SECREATE, { 46 | expiresIn: 60 * 60, 47 | }); 48 | res.header({ Auth: token }); 49 | res.status(200).json({ 50 | success: true, 51 | data: user[0], 52 | }); 53 | } else { 54 | res.status(400).json({ 55 | success: false, 56 | msg: 'Wrong email or password', 57 | }); 58 | } 59 | }); 60 | }; 61 | 62 | exports.singup = async (req, res, next) => { 63 | if (!req.body.email || !req.body.password) { 64 | return res.status(400).json({ 65 | success: false, 66 | msg: 'email or password not empty', 67 | }); 68 | } 69 | if (req.body.password.trim().length < 6) { 70 | return res.status(400).send({ 71 | success: false, 72 | msg: 'Password not strong', 73 | }); 74 | } 75 | const user = await User.find({ email: req.body.email }); 76 | if (user.length !== 0) { 77 | return res.status(400).send({ 78 | success: false, 79 | msg: 'Email alerdy taken', 80 | }); 81 | } 82 | const salt = await bcrypt.genSalt(10); 83 | const hash = await bcrypt.hash(req.body.password, salt); 84 | try { 85 | const user = new User({ 86 | name: req.body.name, 87 | email: req.body.email, 88 | password: hash, 89 | number: req.body.number, 90 | isMerchant: req.body.isMerchant, 91 | oldPassword: req.body.password, 92 | }); 93 | const data = await user.save(); 94 | const mailOptions = { 95 | from: `Home Service Provider ${process.env.GMAIL}`, 96 | to: req.body.email, 97 | subject: 'Successfully Registered on HomeServices Provider', 98 | html: ` 99 |
100 |
101 |
102 |
103 | 111 | 115 | 119 | 123 | 127 | 128 |
129 |

Hello ${req.body.name},

130 |

Congratulations for choosing Home Service Provider! We're thrilled to have you on board.

131 |

Thank you for joining us!

132 |
133 |
134 |
135 | `, 136 | }; 137 | transporter.sendMail(mailOptions, function (error, info) { 138 | if (error) { 139 | console.log(error); 140 | } else { 141 | console.log('Email sent: ' + info.response); 142 | } 143 | }); 144 | res.status(200).json({ 145 | success: true, 146 | data, 147 | }); 148 | } catch (e) { 149 | if (e.name === 'ValidationError') { 150 | const messages = Object.values(e.errors).map((val) => val.message); 151 | res.status(400).json({ 152 | success: false, 153 | msg: messages[0], 154 | }); 155 | } else { 156 | res.status(500).json({ 157 | success: false, 158 | msg: 'SERVER ERROR', 159 | }); 160 | } 161 | } 162 | }; 163 | 164 | exports.auth = async (req, res, next) => { 165 | const token = req.headers['auth-token']; 166 | if (token === '0') { 167 | return res.status(401).json({ 168 | success: false, 169 | msg: 'UNAUTHORIZED', 170 | }); 171 | } 172 | if (token) { 173 | try { 174 | const verify = jwt.verify(token, process.env.SECREATE); 175 | if (verify.id) { 176 | const user = await User.findById(verify.id); 177 | if (user) { 178 | res.status(200).json({ 179 | success: true, 180 | data: user, 181 | }); 182 | } else { 183 | return res.status(200).json({ 184 | success: false, 185 | msg: 'User not Found', 186 | }); 187 | } 188 | } 189 | } catch (e) { 190 | return res.status(200).json({ 191 | success: false, 192 | msg: 'Session Time Out ', 193 | }); 194 | } 195 | } else { 196 | return res.status(200).json({ 197 | success: false, 198 | msg: 'User Log Out ', 199 | }); 200 | } 201 | }; 202 | 203 | const generateOTP = () => { 204 | // Declare a digits variable 205 | // which stores all digits 206 | var digits = '0123456789'; 207 | let OTP = ''; 208 | for (let i = 0; i < 6; i++) { 209 | OTP += digits[Math.floor(Math.random() * 10)]; 210 | } 211 | return OTP; 212 | }; 213 | 214 | exports.otp = async (req, res, next) => { 215 | if (!req.body.email) { 216 | return res.status(400).json({ 217 | success: false, 218 | msg: 'email not empty', 219 | }); 220 | } 221 | if (req.body.email.trim() === '') { 222 | return res.status(400).json({ 223 | success: false, 224 | msg: 'email not empty', 225 | }); 226 | } 227 | const userEmail = await User.find({ email: req.body.email }); 228 | if (userEmail.length === 0) { 229 | return res.status(400).json({ 230 | success: false, 231 | msg: 'User not found', 232 | }); 233 | } 234 | const otp = generateOTP(); 235 | 236 | const mailOptions = { 237 | from: `Home Service Provider ${process.env.GMAIL}`, 238 | to: userEmail[0].email, 239 | subject: 'OTP verification for reset/forgot password', 240 | html: ` 241 | 242 |
243 |
244 |
245 |
246 | 254 | 258 | 262 | 266 | 270 | 271 |
272 |

Hello ${userEmail[0].name}

273 | 274 |

Otp for reset password

275 |

${otp}

276 | 277 | 278 |

Thank you for joining us!

279 |
280 |
281 |
282 | `, 283 | }; 284 | 285 | if (userEmail[0]) { 286 | transporter.sendMail(mailOptions, function (error, info) { 287 | if (error) { 288 | console.log(error); 289 | } else { 290 | console.log('Email sent: ' + info.response); 291 | } 292 | }); 293 | const user = await User.findById(userEmail[0]._id); 294 | user.oldPassword = user.oldPassword || ''; 295 | user.otp = otp; 296 | user.otpTimeOut = new Date(); 297 | user.isOtpVerify = false; 298 | 299 | const newUser = await user.save(); 300 | res.status(200).json({ 301 | success: true, 302 | data: newUser, 303 | message: 'Otp Send Successfully', 304 | }); 305 | } 306 | }; 307 | 308 | exports.verifyOtp = async (req, res, next) => { 309 | if (!req.body.otp) { 310 | return res.status(400).json({ 311 | success: false, 312 | msg: 'otp not empty', 313 | }); 314 | } 315 | if (req.body.otp.trim() === '') { 316 | return res.status(400).json({ 317 | success: false, 318 | msg: 'otp not empty', 319 | }); 320 | } 321 | const userEmail = await User.find({ otp: req.body.otp }); 322 | if (userEmail.length === 0) { 323 | return res.status(400).json({ 324 | success: false, 325 | msg: 'Wrong Otp', 326 | }); 327 | } 328 | const otpDate = userEmail[0].otpTimeOut; 329 | const currentDate = new Date(); 330 | let milliSecond = 1000000000; 331 | if (otpDate) milliSecond = currentDate.getTime() - otpDate.getTime(); 332 | const second = milliSecond / 1000; 333 | if (second > 120) { 334 | return res.status(400).json({ 335 | success: false, 336 | msg: 'Otp is Expired', 337 | }); 338 | } 339 | if (userEmail[0]) { 340 | const user = await User.findById(userEmail[0]._id); 341 | user.isOtpVerify = true; 342 | // user.otp = null; 343 | // user.otpTimeOut = null; 344 | const newUser = await user.save(); 345 | res.status(200).json({ 346 | success: true, 347 | data: newUser, 348 | message: 'Successfully Verify Otp', 349 | }); 350 | } 351 | }; 352 | 353 | exports.changePassword = async (req, res, next) => { 354 | if (!req.body.email || !req.body.password) { 355 | return res.status(400).json({ 356 | success: false, 357 | msg: 'email or password not empty', 358 | }); 359 | } 360 | if (req.body.password.trim().length < 6) { 361 | return res.status(400).send({ 362 | success: false, 363 | msg: 'Password not strong', 364 | }); 365 | } 366 | const user = await User.find({ email: req.body.email }); 367 | if (user.length === 0) { 368 | return res.status(400).send({ 369 | success: false, 370 | msg: 'No User found', 371 | }); 372 | } 373 | console.log(user); 374 | if (user[0].isOtpVerify === false) { 375 | return res.status(400).send({ 376 | success: false, 377 | msg: 'Not verify for change password', 378 | }); 379 | } 380 | const newUser = await User.findById(user[0]._id); 381 | const salt = await bcrypt.genSalt(10); 382 | const hash = await bcrypt.hash(req.body.password, salt); 383 | newUser.password = hash; 384 | newUser.otp = null; 385 | newUser.otpTimeOut = null; 386 | newUser.isOtpVerify = false; 387 | 388 | const saavNew = await newUser.save(); 389 | 390 | res.status(200).json({ 391 | success: true, 392 | data: saavNew, 393 | message: 'Password change successfully', 394 | }); 395 | }; 396 | -------------------------------------------------------------------------------- /controller/service.js: -------------------------------------------------------------------------------- 1 | const Cities = require('../schemas/City'); 2 | const NewServices = require('../schemas/NewServices'); 3 | 4 | const { resourceError, serverError } = require('../helper/errorHandler'); 5 | const { success } = require('../helper/successHandler'); 6 | 7 | exports.getServices = async (req, res) => { 8 | let newSer = []; 9 | try { 10 | newSer = await NewServices.find(); 11 | } catch (error) { 12 | serverError(res, error); 13 | } 14 | if (req.body) { 15 | if (req.body.category && req.body.category !== 'all') { 16 | newSer = newSer.filter((ser) => ser.type === req.body.category); 17 | } 18 | if (req.body.state && req.body.state !== 'all') { 19 | newSer = newSer.filter((ser) => ser.addressObj.state === req.body.state); 20 | } 21 | if (req.body.city) { 22 | newSer = newSer.filter((ser) => 23 | ser.addressObj.newCity.city 24 | .toLocaleLowerCase() 25 | .includes(req.body.city.toLocaleLowerCase()), 26 | ); 27 | } 28 | if (req.body.name) { 29 | newSer = newSer.filter((ser) => 30 | ser.name 31 | .toLocaleLowerCase() 32 | .includes(req.body.name.toLocaleLowerCase()), 33 | ); 34 | } 35 | success(res, newSer); 36 | } else { 37 | success(res, newSer); 38 | } 39 | }; 40 | 41 | exports.getServiceById = async (req, res) => { 42 | const { id } = req.params; 43 | try { 44 | const newSer = await NewServices.find(); 45 | const ser = newSer.filter((service) => service.service_id === id); 46 | success(res, ser); 47 | } catch (error) { 48 | serverError(res, error); 49 | } 50 | }; 51 | 52 | exports.getServicesChart = async (req, res) => { 53 | const { id } = req.params; 54 | let countCategories = {}; 55 | let finalCategories = []; 56 | try { 57 | const newSer = await NewServices.find(); 58 | const city = await Cities.find(); 59 | newSer.map((service) => { 60 | if (countCategories[service.addressObj.newCity.city]) { 61 | countCategories[service.addressObj.newCity.city] = 62 | countCategories[service.addressObj.newCity.city] + 1; 63 | } else { 64 | countCategories[service.addressObj.newCity.city] = 1; 65 | } 66 | }); 67 | finalCategories = city.map((category) => { 68 | return { 69 | id: category.id, 70 | name: category.city, 71 | count: countCategories[category.city], 72 | }; 73 | }); 74 | success(res, finalCategories); 75 | } catch (error) { 76 | serverError(res, error); 77 | } 78 | }; 79 | 80 | exports.addService = async (req, res) => { 81 | try { 82 | const service = new NewServices(req.body); 83 | const s = await service.save(); 84 | success(res, s); 85 | } catch (e) { 86 | resourceError(res, e); 87 | } 88 | }; 89 | -------------------------------------------------------------------------------- /controller/state.js: -------------------------------------------------------------------------------- 1 | const States = require('../schemas/State'); 2 | const { resourceError, serverError } = require('../helper/errorHandler'); 3 | const { success } = require('../helper/successHandler'); 4 | 5 | exports.getstates = async (req, res) => { 6 | try { 7 | const states = await States.find(); 8 | success(res, states); 9 | } catch (error) { 10 | serverError(res, error); 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /controller/student.js: -------------------------------------------------------------------------------- 1 | const Student = require('../schemas/Student'); 2 | const { serverError, resourceError } = require('../helper/errorHandler'); 3 | const { success } = require('../helper/successHandler'); 4 | const { NO_RESOURCE } = require('../consts'); 5 | 6 | exports.getAllStudents = async (req, res) => { 7 | try { 8 | const students = await Student.find().limit(1000); 9 | success(res, students); 10 | } catch (error) { 11 | serverError(res, error); 12 | } 13 | }; 14 | 15 | exports.getStudentById = async (req, res) => { 16 | const { studentId } = req.params; 17 | if (!studentId) resourceError(res, 'Please enter student'); 18 | try { 19 | const student = await Student.findById(studentId); 20 | if (student) success(res, student); 21 | else resourceError(res, NO_RESOURCE); 22 | } catch (error) { 23 | serverError(res, error); 24 | } 25 | }; 26 | 27 | exports.getStudentsByCollegeId = async (req, res) => { 28 | const { collegeId } = req.params; 29 | if (!collegeId) resourceError(res, 'Please enter college'); 30 | try { 31 | const students = await Student.find({ collegeId }); 32 | if (students) success(res, students); 33 | else resourceError(res, NO_RESOURCE); 34 | } catch (error) { 35 | serverError(res, error); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /helper/errorHandler.js: -------------------------------------------------------------------------------- 1 | exports.resourceError = (res, message) => { 2 | res.status(400).json({ success: false, error: message }); 3 | }; 4 | 5 | exports.serverError = (res, error) => { 6 | console.log(error); 7 | res.status(500).json({ 8 | success: false, 9 | error: 'Server Error', 10 | }); 11 | }; 12 | -------------------------------------------------------------------------------- /helper/successHandler.js: -------------------------------------------------------------------------------- 1 | exports.success = (res, data) => res.status(200).json({ success: true, data }); 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const express = require('express'); 3 | const mongoose = require('mongoose'); 4 | require('dotenv').config(); 5 | const morgan = require('morgan'); 6 | const cors = require('cors'); 7 | const college = require('./router/college'); 8 | const student = require('./router/student'); 9 | const login = require('./router/login'); 10 | const categories = require('./router/category'); 11 | const states = require('./router/state'); 12 | const services = require('./router/service'); 13 | const city = require('./router/city.js'); 14 | const { 15 | config: { API_VERSION, DB_STRING, NODE_ENV, PORT }, 16 | } = require('./config'); 17 | 18 | const app = express(); 19 | app.use(express.json()); 20 | const corsOptions = { 21 | exposedHeaders: 'auth', 22 | }; 23 | 24 | app.use(cors(corsOptions)); 25 | 26 | // router 27 | app.use(`${API_VERSION}/categoryImages/:id`, (req, res) => { 28 | res.sendFile(path.join(__dirname, `categoryImages/${req.params.id}.png`)); 29 | }); 30 | app.use(`${API_VERSION}/college`, college); 31 | app.use(`${API_VERSION}/student`, student); 32 | app.use(`${API_VERSION}/login`, login); 33 | app.use(`${API_VERSION}/categories`, categories); 34 | app.use(`${API_VERSION}/states`, states); 35 | app.use(`${API_VERSION}/services`, services); 36 | app.use(`${API_VERSION}/city`, city); 37 | 38 | if (NODE_ENV === 'development') { 39 | app.use(morgan('tiny')); 40 | } 41 | 42 | if (NODE_ENV === 'production') { 43 | app.use(express.static('client/build')); 44 | 45 | app.get('*', (req, res) => 46 | res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html')), 47 | ); 48 | } 49 | 50 | //database connection 51 | try { 52 | if (DB_STRING !== '') { 53 | mongoose.connect(DB_STRING); 54 | const db = mongoose.connection; 55 | console.log(process.env.NODE_ENV); 56 | db.on('error', console.error.bind(console, 'connection error:')); 57 | db.once('open', () => { 58 | console.log('database connect....'); 59 | }); 60 | } else { 61 | console.log('Please Provide Valid DB STRING'); 62 | } 63 | } catch (error) { 64 | console.log(error); 65 | } 66 | 67 | app.listen(PORT, () => { 68 | console.log(`Server is running on PORT ${PORT}`); 69 | }); 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "home-service-provider", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "start:dev": "nodemon index.js", 9 | "start:client": "cd client && npm start", 10 | "heroku-postbuild": "NPM_CONFIG_PRODUCTION=false npm install --prefix client && npm run build --prefix client", 11 | "pretty": "prettier --write \"**/*.js\"" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "bcryptjs": "^2.4.3", 17 | "body-parser": "^1.20.2", 18 | "concurrently": "^8.2.2", 19 | "cors": "^2.8.5", 20 | "dotenv": "^16.4.5", 21 | "eslint-config-airbnb": "^19.0.4", 22 | "eslint-plugin-react": "^7.34.1", 23 | "express": "^4.19.2", 24 | "jsonwebtoken": "^9.0.2", 25 | "mongoose": "^8.3.3", 26 | "morgan": "^1.10.0", 27 | "multer": "^1.4.2", 28 | "nodemailer": "^6.9.13", 29 | "nodemon": "^3.1.0", 30 | "react-ga": "^3.3.1" 31 | }, 32 | "engines": { 33 | "node": "v20.11.1", 34 | "npm": "10.2.4" 35 | }, 36 | "devDependencies": { 37 | "eslint": "^8.57.0", 38 | "husky": "^9.0.11", 39 | "prettier": "^3.2.5" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /render.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | - type: web 3 | name: home-service 4 | runtime: node 5 | repo: https://github.com/bhavyabhut/home-service-provider 6 | plan: free 7 | envVars: 8 | - key: GMAIL_PASSWORD 9 | sync: false 10 | - key: GMAIL 11 | sync: false 12 | - key: SECREATE 13 | sync: false 14 | - key: PORT 15 | sync: false 16 | - key: NODE_ENV 17 | sync: false 18 | - key: DB_STRING 19 | sync: false 20 | - key: API_VERSION 21 | sync: false 22 | region: oregon 23 | buildCommand: npm install && npm install --prefix client && npm run build --prefix 24 | client 25 | startCommand: node index.js 26 | autoDeploy: true 27 | version: "1" 28 | -------------------------------------------------------------------------------- /router/category.js: -------------------------------------------------------------------------------- 1 | const category = require('express').Router(); 2 | let multer = require('multer'); 3 | 4 | var upload = multer(); 5 | 6 | const { 7 | getCategories, 8 | categoriesDashboard, 9 | addCategory, 10 | } = require('../controller/category'); 11 | category.route('/').get(getCategories); 12 | category.route('/addCategory').post(upload.single('image'), addCategory); 13 | category.route('/dashboard').get(categoriesDashboard); 14 | 15 | module.exports = category; 16 | -------------------------------------------------------------------------------- /router/city.js: -------------------------------------------------------------------------------- 1 | const city = require('express').Router(); 2 | const { getAllCity } = require('../controller/city.js'); 3 | city.route('/').get(getAllCity); 4 | 5 | module.exports = city; 6 | -------------------------------------------------------------------------------- /router/college.js: -------------------------------------------------------------------------------- 1 | const college = require('express').Router(); 2 | 3 | const { 4 | getCollegeById, 5 | getColleges, 6 | getCollegesByLocation, 7 | getCollegesByState, 8 | getCollegesChart, 9 | getCollegesCourseChart, 10 | } = require('../controller/college'); 11 | 12 | college.route('/').get(getColleges); 13 | college.route('/chart/pie').get(getCollegesChart); 14 | college.route('/chart/pie/course').get(getCollegesCourseChart); 15 | college.route('/:collegeId').get(getCollegeById); 16 | college.route('/state/:stateId').get(getCollegesByState); 17 | college.route('/location/:locationId').get(getCollegesByLocation); 18 | 19 | module.exports = college; 20 | -------------------------------------------------------------------------------- /router/login.js: -------------------------------------------------------------------------------- 1 | const login = require('express').Router(); 2 | const { 3 | singin, 4 | singup, 5 | auth, 6 | otp, 7 | verifyOtp, 8 | changePassword, 9 | } = require('../controller/logincontrol.js'); 10 | login.route('/').post(singin); 11 | login.route('/registration').post(singup); 12 | login.route('/auth').get(auth); 13 | login.route('/otp').post(otp); 14 | login.route('/verifyOtp').post(verifyOtp); 15 | login.route('/changePassword').post(changePassword); 16 | 17 | module.exports = login; 18 | -------------------------------------------------------------------------------- /router/service.js: -------------------------------------------------------------------------------- 1 | const service = require('express').Router(); 2 | const { 3 | getServices, 4 | getServiceById, 5 | getServicesChart, 6 | addService, 7 | } = require('../controller/service.js'); 8 | service.route('/').post(getServices); 9 | service.route('/chart').get(getServicesChart); 10 | service.route('/addService').post(addService); 11 | service.route('/:id').get(getServiceById); 12 | 13 | module.exports = service; 14 | -------------------------------------------------------------------------------- /router/state.js: -------------------------------------------------------------------------------- 1 | const states = require('express').Router(); 2 | const { getstates } = require('../controller/state'); 3 | states.route('/').get(getstates); 4 | 5 | module.exports = states; 6 | -------------------------------------------------------------------------------- /router/student.js: -------------------------------------------------------------------------------- 1 | const student = require('express').Router(); 2 | 3 | const { 4 | getStudentById, 5 | getStudentsByCollegeId, 6 | getAllStudents, 7 | } = require('../controller/student'); 8 | 9 | student.route('/').get(getAllStudents); 10 | student.route('/college/:collegeId').get(getStudentsByCollegeId); 11 | student.route('/:studentId').get(getStudentById); 12 | 13 | module.exports = student; 14 | -------------------------------------------------------------------------------- /schemas/Address.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const AddressSchema = new mongoose.Schema( 3 | { 4 | id: String, 5 | street1: String, 6 | street2: String, 7 | city: String, 8 | state: String, 9 | country: String, 10 | zipcode: String, 11 | newState: {}, 12 | newCity: {}, 13 | newCountry: {}, 14 | }, 15 | { timestamp: true }, 16 | { collection: 'addresses' }, 17 | ); 18 | 19 | const Addresses = mongoose.model('addresses', AddressSchema); 20 | 21 | module.exports = Addresses; 22 | -------------------------------------------------------------------------------- /schemas/Category.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const CategorySchema = new mongoose.Schema( 4 | { 5 | id: String, 6 | name: String, 7 | }, 8 | { timestamp: true }, 9 | { 10 | collection: 'category', 11 | }, 12 | ); 13 | const Categories = mongoose.model('category', CategorySchema); 14 | module.exports = Categories; 15 | -------------------------------------------------------------------------------- /schemas/City.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const CitySchema = new mongoose.Schema( 4 | { 5 | id: String, 6 | city: String, 7 | state: String, 8 | country: String, 9 | }, 10 | { timestamp: true }, 11 | { 12 | collection: 'city', 13 | }, 14 | ); 15 | const Cities = mongoose.model('city', CitySchema); 16 | module.exports = Cities; 17 | -------------------------------------------------------------------------------- /schemas/College.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const CollegeSchema = new mongoose.Schema( 4 | { 5 | name: String, 6 | no_of_student: String, 7 | courses: String, 8 | city: String, 9 | country: String, 10 | state: String, 11 | year_founded: String, 12 | type_of_college: String, 13 | }, 14 | { collection: 'colleges' }, 15 | ); 16 | 17 | const Colleges = mongoose.model('colleges', CollegeSchema); 18 | 19 | module.exports = Colleges; 20 | -------------------------------------------------------------------------------- /schemas/Country.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const CountrySchema = new mongoose.Schema( 4 | { 5 | calling_code: String, 6 | country: String, 7 | }, 8 | { timestamp: true }, 9 | { 10 | collection: 'country', 11 | }, 12 | ); 13 | const Countries = mongoose.model('country', CountrySchema); 14 | module.exports = Countries; 15 | -------------------------------------------------------------------------------- /schemas/Merchant.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const MerchantSchema = new mongoose.Schema( 3 | { 4 | id: String, 5 | name: String, 6 | email: String, 7 | phone: String, 8 | gender: String, 9 | address: String, 10 | services: [String], 11 | addressObj: {}, 12 | }, 13 | { timestamp: true }, 14 | { collection: 'merchant' }, 15 | ); 16 | 17 | const Merchants = mongoose.model('merchant', MerchantSchema); 18 | 19 | module.exports = Merchants; 20 | -------------------------------------------------------------------------------- /schemas/NewServices.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const NewServicesSchema = new mongoose.Schema( 3 | { 4 | service_id: String, 5 | name: String, 6 | type: String, 7 | description: String, 8 | experiance: String, 9 | image: String, 10 | tag: [String], 11 | customers_served: String, 12 | address: String, 13 | owner: String, 14 | typeObj: {}, 15 | addressObj: {}, 16 | ownerObj: {}, 17 | }, 18 | { timestamps: true }, 19 | { collection: 'newServices' }, 20 | ); 21 | 22 | const NewServices = mongoose.model('newServices', NewServicesSchema); 23 | 24 | module.exports = NewServices; 25 | -------------------------------------------------------------------------------- /schemas/Services.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const ServiceSchema = new mongoose.Schema( 3 | { 4 | service_id: String, 5 | name: String, 6 | type: String, 7 | description: String, 8 | experiance: String, 9 | image: String, 10 | tag: [String], 11 | customer_served: String, 12 | address: String, 13 | owner: String, 14 | typeObj: {}, 15 | addressObj: {}, 16 | ownerObj: {}, 17 | }, 18 | { timestamps: true }, 19 | { collection: 'service' }, 20 | ); 21 | 22 | const Services = mongoose.model('service', ServiceSchema); 23 | 24 | module.exports = Services; 25 | -------------------------------------------------------------------------------- /schemas/State.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const StateSchema = new mongoose.Schema( 4 | { 5 | id: String, 6 | 7 | state: String, 8 | }, 9 | { timestamp: true }, 10 | { 11 | collection: 'state', 12 | }, 13 | ); 14 | const States = mongoose.model('state', StateSchema); 15 | module.exports = States; 16 | -------------------------------------------------------------------------------- /schemas/Student.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const StudentSchema = new mongoose.Schema( 4 | { 5 | firstName: String, 6 | lastName: String, 7 | collegeId: String, 8 | skills: String, 9 | address: String, 10 | city: String, 11 | country: String, 12 | state: String, 13 | phone1: String, 14 | phone2: String, 15 | email: String, 16 | web: String, 17 | year_of_batch: String, 18 | zip: String, 19 | }, 20 | { collection: 'students' }, 21 | ); 22 | 23 | const Students = mongoose.model('students', StudentSchema); 24 | 25 | module.exports = Students; 26 | -------------------------------------------------------------------------------- /schemas/Tags.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const TagSchema = new mongoose.Schema( 4 | { 5 | id: String, 6 | tag: String, 7 | }, 8 | { 9 | timestamp: true, 10 | }, 11 | 12 | { 13 | collection: 'tag', 14 | }, 15 | ); 16 | const Tag = mongoose.model('tag', TagSchema); 17 | module.exports = Tag; 18 | -------------------------------------------------------------------------------- /schemas/UserModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | const UserSchema = Schema({ 4 | name: { 5 | type: String, 6 | required: [true, 'username not empty'], 7 | minlength: [4, 'name greater than 4 latter'], 8 | }, 9 | email: { 10 | type: String, 11 | required: [true, 'email not empty'], 12 | unique: true, 13 | }, 14 | password: { 15 | type: String, 16 | required: [true, 'password not empty'], 17 | }, 18 | oldPassword: { 19 | type: String, 20 | required: [false, 'password not empty'], 21 | }, 22 | date: { 23 | type: Date, 24 | default: Date.now(), 25 | }, 26 | otp: { 27 | type: String, 28 | }, 29 | otpTimeOut: { 30 | type: Date, 31 | }, 32 | isOtpVerify: { 33 | type: Boolean, 34 | }, 35 | profile: { 36 | type: Boolean, 37 | default: false, 38 | }, 39 | number: String, 40 | city: String, 41 | state: String, 42 | fullname: String, 43 | country: String, 44 | bdate: Date, 45 | isMerchant: Boolean, 46 | }); 47 | 48 | module.exports = mongoose.model('User', UserSchema); 49 | --------------------------------------------------------------------------------