├── server
├── Procfile
├── public
│ └── favicon.ico
├── socket
│ ├── global.js
│ ├── init
│ │ ├── authorizationInit.js
│ │ ├── loggedusersInit.js
│ │ ├── notificationInit.js
│ │ └── chatInit.js
│ ├── index.js
│ ├── events
│ │ ├── loggedusersEvent.js
│ │ └── authorizationEvents.js
│ └── utils
│ │ ├── chatUtils.js
│ │ ├── authorizationUtils.js
│ │ └── index.js
├── .env
├── src
│ ├── configs
│ │ ├── config.js
│ │ ├── errorHandler.js
│ │ ├── passport.js
│ │ └── socket.js
│ ├── routes
│ │ ├── authRoutes.js
│ │ ├── adminRoutes
│ │ │ ├── index.js
│ │ │ ├── categoryRoutes.js
│ │ │ └── userRoutes.js
│ │ ├── index.js
│ │ └── articleRoutes.js
│ ├── models
│ │ ├── adminModel
│ │ │ └── categoryModel.js
│ │ ├── articleModel.js
│ │ └── userModel.js
│ ├── db
│ │ └── connect.js
│ ├── middleware
│ │ └── authorization.js
│ └── controllers
│ │ ├── adminControllers
│ │ └── categoryController.js
│ │ └── authController.js
├── .gitignore
├── .eslintrc.json
├── socketApp.js
├── package.json
├── app.js
└── README.md
├── client
├── src
│ ├── features
│ │ ├── profile
│ │ │ ├── components
│ │ │ │ └── Repositories.js
│ │ │ └── index.jsx
│ │ ├── common
│ │ │ ├── components
│ │ │ │ ├── NotificationBodyRightDrawer.js
│ │ │ │ └── ConfirmationModalBody.js
│ │ │ ├── rightDrawerSlice.js
│ │ │ ├── headerSlice.js
│ │ │ └── modalSlice.js
│ │ ├── user
│ │ │ ├── components
│ │ │ │ └── TemplatePointers.js
│ │ │ ├── LandingIntro.js
│ │ │ └── Login.js
│ │ ├── dashboard
│ │ │ ├── components
│ │ │ │ ├── AmountStats.js
│ │ │ │ ├── PageStats.js
│ │ │ │ ├── DashboardStats.js
│ │ │ │ ├── LineChart.js
│ │ │ │ ├── BarChart.js
│ │ │ │ ├── Toolbar.js
│ │ │ │ ├── UserChannels.js
│ │ │ │ ├── DoughnutChart.js
│ │ │ │ ├── DashboardTopBar.js
│ │ │ │ └── ArticleCard.js
│ │ │ └── index.js
│ │ ├── leads
│ │ │ ├── leadSlice.js
│ │ │ └── components
│ │ │ │ └── AddLeadModalBody.js
│ │ ├── charts
│ │ │ ├── components
│ │ │ │ ├── LineChart.js
│ │ │ │ ├── BarChart.js
│ │ │ │ ├── ScatterChart.js
│ │ │ │ ├── StackBarChart.js
│ │ │ │ ├── DoughnutChart.js
│ │ │ │ └── PieChart.js
│ │ │ └── index.js
│ │ ├── settings
│ │ │ └── changepassword
│ │ │ │ └── index.js
│ │ └── integration
│ │ │ └── index.js
│ ├── components
│ │ ├── Typography
│ │ │ ├── Title.js
│ │ │ ├── HelperText.js
│ │ │ ├── ErrorText.js
│ │ │ └── Subtitle.js
│ │ ├── Button.jsx
│ │ ├── Input
│ │ │ ├── Input.js
│ │ │ ├── SearchBar.js
│ │ │ ├── StanSearchBar.js
│ │ │ ├── SelectBoxSmall.js
│ │ │ ├── SelectBoxBig.js
│ │ │ ├── TextAreaInput.js
│ │ │ ├── InputText.js
│ │ │ ├── Inputdisabled.js
│ │ │ ├── BigInputText.js
│ │ │ ├── ToogleInput.js
│ │ │ └── SelectBox.js
│ │ ├── Logo.jsx
│ │ ├── CalendarView
│ │ │ └── util.js
│ │ ├── Cards
│ │ │ └── TitleCard.js
│ │ └── Avatar.jsx
│ ├── utils
│ │ ├── ErrorText.js
│ │ ├── globalConstantUtil.js
│ │ ├── requestServer.js
│ │ └── InputText.js
│ ├── containers
│ │ ├── SuspenseContent.js
│ │ ├── PageContent.js
│ │ ├── ModalLayout.js
│ │ ├── SidebarSubmenu.js
│ │ ├── Layout.js
│ │ ├── RightSidebar.js
│ │ └── LeftSidebar.js
│ ├── setupTests.js
│ ├── App.test.js
│ ├── pages
│ │ ├── ForgotPassword.js
│ │ ├── protected
│ │ │ ├── Charts.js
│ │ │ ├── Leads.js
│ │ │ ├── Dashboard.js
│ │ │ ├── Integration.js
│ │ │ ├── Transactions.js
│ │ │ ├── 404.js
│ │ │ ├── Welcome.js
│ │ │ └── Blank.js
│ │ ├── Settings
│ │ │ ├── Team.js
│ │ │ ├── ChangePassword.js
│ │ │ └── ProfileSettings.js
│ │ ├── Component
│ │ │ └── Input.jsx
│ │ ├── Admin
│ │ │ ├── UserManage
│ │ │ │ ├── EditAccount
│ │ │ │ │ ├── index.jsx
│ │ │ │ │ └── EditManage
│ │ │ │ │ │ └── index.jsx
│ │ │ │ ├── index.jsx
│ │ │ │ └── UserManagePanel
│ │ │ │ │ └── TopSideButtons
│ │ │ │ │ └── index.js
│ │ │ └── Category
│ │ │ │ ├── index.jsx
│ │ │ │ └── components
│ │ │ │ ├── TableCategory.js
│ │ │ │ └── InputCategory.js
│ │ ├── Article
│ │ │ ├── Draft.jsx
│ │ │ ├── MyArticle.jsx
│ │ │ └── FavouriteArticle.jsx
│ │ └── Signin
│ │ │ └── index.jsx
│ ├── reportWebVitals.js
│ ├── redux
│ │ ├── init.js
│ │ ├── store.js
│ │ ├── auth.js
│ │ └── adminSlice.js
│ ├── App.css
│ ├── index.js
│ ├── index.css
│ ├── App.js
│ ├── logo.svg
│ └── routes
│ │ └── index.js
├── .env.template
├── .env
├── public
│ ├── intro.png
│ ├── logo.jpg
│ ├── robots.txt
│ ├── default.jpg
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── apple-touch-icon.png
│ ├── assets
│ │ ├── avatars
│ │ │ ├── ava.png
│ │ │ ├── avatar.jpg
│ │ │ ├── default.jpg
│ │ │ ├── avatar-10.jpg
│ │ │ ├── avatar-12.jpg
│ │ │ ├── avatar-14.jpg
│ │ │ ├── avatar-17.jpg
│ │ │ ├── 66b4024cbaf1712da9855432.14.webp
│ │ │ └── 668412781664e859963a7068.135434950.jpeg
│ │ └── images
│ │ │ ├── logo.png
│ │ │ ├── default.jpg
│ │ │ └── background_outrun.png
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ ├── manifest.json
│ └── index.html
├── postcss.config.js
├── .gitignore
├── tailwind.config.js
├── LICENSE.md
├── package.json
└── README.md
├── .github
└── ISSUE_TEMPLATE
│ ├── custom.md
│ ├── feature_request.md
│ └── bug_report.md
├── .gitignore
├── API Design.md
├── LICENSE
├── Model Design.md
├── package.json
└── README.md
/server/Procfile:
--------------------------------------------------------------------------------
1 | web: NODE_ENV=production node app.js
--------------------------------------------------------------------------------
/client/src/features/profile/components/Repositories.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/.env.template:
--------------------------------------------------------------------------------
1 | REACT_APP_BASE_URL=https://reqres.in/
2 |
--------------------------------------------------------------------------------
/client/.env:
--------------------------------------------------------------------------------
1 | REACT_APP_BASE_URL=http://localhost:4000
2 | PORT = 3001
3 |
--------------------------------------------------------------------------------
/client/public/intro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/intro.png
--------------------------------------------------------------------------------
/client/public/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/logo.jpg
--------------------------------------------------------------------------------
/client/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/client/public/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/default.jpg
--------------------------------------------------------------------------------
/client/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/favicon.ico
--------------------------------------------------------------------------------
/client/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/logo192.png
--------------------------------------------------------------------------------
/client/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/logo512.png
--------------------------------------------------------------------------------
/server/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/server/public/favicon.ico
--------------------------------------------------------------------------------
/client/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/favicon-16x16.png
--------------------------------------------------------------------------------
/client/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/favicon-32x32.png
--------------------------------------------------------------------------------
/client/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/client/public/assets/avatars/ava.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/assets/avatars/ava.png
--------------------------------------------------------------------------------
/client/public/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/assets/images/logo.png
--------------------------------------------------------------------------------
/client/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/client/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/client/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/client/public/assets/avatars/avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/assets/avatars/avatar.jpg
--------------------------------------------------------------------------------
/client/public/assets/avatars/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/assets/avatars/default.jpg
--------------------------------------------------------------------------------
/client/public/assets/images/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/assets/images/default.jpg
--------------------------------------------------------------------------------
/server/socket/global.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | relax: {
3 | roomUniqueIndex: 0,
4 | rooms: {},
5 | savedRoomData: {},
6 | },
7 | }
--------------------------------------------------------------------------------
/client/public/assets/avatars/avatar-10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/assets/avatars/avatar-10.jpg
--------------------------------------------------------------------------------
/client/public/assets/avatars/avatar-12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/assets/avatars/avatar-12.jpg
--------------------------------------------------------------------------------
/client/public/assets/avatars/avatar-14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/assets/avatars/avatar-14.jpg
--------------------------------------------------------------------------------
/client/public/assets/avatars/avatar-17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/assets/avatars/avatar-17.jpg
--------------------------------------------------------------------------------
/client/public/assets/images/background_outrun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/assets/images/background_outrun.png
--------------------------------------------------------------------------------
/client/public/assets/avatars/66b4024cbaf1712da9855432.14.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/assets/avatars/66b4024cbaf1712da9855432.14.webp
--------------------------------------------------------------------------------
/server/.env:
--------------------------------------------------------------------------------
1 | # APP ENVIRONMENT VARIABLES
2 | APP_NAME =
3 | APP_PORT = 4000
4 | APP_BASE_URL =
5 | APP_API_PREFIX =
6 |
7 | # DB ENVIRONMENT VARIABLES
8 | MONGO_URI =
9 |
--------------------------------------------------------------------------------
/client/public/assets/avatars/668412781664e859963a7068.135434950.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiny7star/finalGoal/HEAD/client/public/assets/avatars/668412781664e859963a7068.135434950.jpeg
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/custom.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Custom issue template
3 | about: Describe this issue template's purpose here.
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
--------------------------------------------------------------------------------
/client/src/components/Typography/Title.js:
--------------------------------------------------------------------------------
1 | function Title({className, children}){
2 | return(
3 |
{children}
4 | )
5 | }
6 |
7 | export default Title
--------------------------------------------------------------------------------
/client/src/utils/ErrorText.js:
--------------------------------------------------------------------------------
1 | function ErrorText({children, Style}) {
2 | return (
3 | {children}
4 | );
5 | }
6 |
7 | export default ErrorText;
--------------------------------------------------------------------------------
/client/src/components/Typography/HelperText.js:
--------------------------------------------------------------------------------
1 | function HelperText({className, children}){
2 | return(
3 | {children}
4 | )
5 | }
6 |
7 | export default HelperText
--------------------------------------------------------------------------------
/client/src/components/Typography/ErrorText.js:
--------------------------------------------------------------------------------
1 | function ErrorText({styleClass, children}){
2 | return(
3 | {children}
4 | )
5 | }
6 |
7 | export default ErrorText
--------------------------------------------------------------------------------
/client/src/components/Typography/Subtitle.js:
--------------------------------------------------------------------------------
1 | function Subtitle({ styleClass, children, onClick }) {
2 | return {children}
;
3 | }
4 |
5 | export default Subtitle;
6 |
--------------------------------------------------------------------------------
/client/src/features/profile/index.jsx:
--------------------------------------------------------------------------------
1 | import ProfileInfo from "./components/ProfileInfo";
2 |
3 | const Profile = () => {
4 | return (
5 | <>
6 |
7 | >
8 | );
9 | };
10 |
11 | export default Profile;
12 |
--------------------------------------------------------------------------------
/server/src/configs/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | MONGOURI: "mongodb://127.0.0.1:27017",
3 | secretOrKey: "secret",
4 | adminData: {
5 | name: "admin",
6 | username: "admin",
7 | password: "admin",
8 | role: "admin",
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/client/src/containers/SuspenseContent.js:
--------------------------------------------------------------------------------
1 | function SuspenseContent(){
2 | return(
3 |
4 | Loading...
5 |
6 | )
7 | }
8 |
9 | export default SuspenseContent
--------------------------------------------------------------------------------
/client/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/client/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 |
15 | npm-debug.log*
16 | yarn-debug.log*
17 | yarn-error.log*
18 |
--------------------------------------------------------------------------------
/server/src/routes/authRoutes.js:
--------------------------------------------------------------------------------
1 | const router = require("express").Router();
2 |
3 | // init controller
4 | const authCtrl = require("../controllers/authController");
5 |
6 | // routers //
7 | router.post("/signup", authCtrl.signup);
8 | router.post("/signin", authCtrl.signin);
9 |
10 | module.exports = router;
11 |
--------------------------------------------------------------------------------
/client/src/pages/ForgotPassword.js:
--------------------------------------------------------------------------------
1 | import { useState, useRef } from "react";
2 | import { Link } from "react-router-dom";
3 | import ForgotPassword from "../features/user/ForgotPassword";
4 | import Login from "../features/user/Login";
5 |
6 | function ExternalPage() {
7 | return (
8 |
9 |
10 |
11 | );
12 | }
13 |
14 | export default ExternalPage;
15 |
--------------------------------------------------------------------------------
/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 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/server/src/models/adminModel/categoryModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | const categorySchema = mongoose.Schema({
4 | title: {
5 | type: String,
6 | required: true,
7 | },
8 | delected: {
9 | type: Date,
10 | default: null,
11 | },
12 | createdAt: {
13 | type: Date,
14 | default: Date.now,
15 | },
16 | });
17 | module.exports = mongoose.model('Category', categorySchema);
18 |
--------------------------------------------------------------------------------
/client/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/server/src/routes/adminRoutes/index.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 |
3 | //init Routes
4 |
5 | const userRoutes = require("./userRoutes");
6 | const categoryRoutes = require("./categoryRoutes");
7 | // email routes //
8 |
9 | //routes container
10 | const router = express.Router();
11 |
12 | router.use("/user", userRoutes);
13 | router.use("/category", categoryRoutes);
14 | // email routes con//
15 |
16 | module.exports = router;
17 |
--------------------------------------------------------------------------------
/client/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | "./src/**/*.{js,jsx,ts,tsx}",
5 | "./node_modules/react-tailwindcss-datepicker/dist/index.esm.js",
6 | ],
7 | darkMode: ["class", '[data-theme="dark"]'],
8 | theme: {
9 | extend: {},
10 | },
11 | plugins: [require("@tailwindcss/typography"), require("daisyui")],
12 | daisyui: {
13 | themes: ["light", "dark"],
14 | },
15 | };
16 |
--------------------------------------------------------------------------------
/client/src/pages/protected/Charts.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useDispatch } from "react-redux";
3 | import Charts from "../../features/charts";
4 | import { setPageTitle } from "../../features/common/headerSlice";
5 |
6 | function InternalPage() {
7 | const dispatch = useDispatch();
8 |
9 | useEffect(() => {
10 | dispatch(setPageTitle({ title: "Analytics" }));
11 | }, []);
12 |
13 | return ;
14 | }
15 |
16 | export default InternalPage;
17 |
--------------------------------------------------------------------------------
/client/src/components/Button.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export const Button = (prop) => {
4 | return (
5 |
12 | );
13 | };
14 |
--------------------------------------------------------------------------------
/client/src/components/Input/Input.js:
--------------------------------------------------------------------------------
1 | function Input({ name, type, placeholder, setHandleKeyDown, onChange, className }) {
2 | return (
3 |
11 | );
12 | }
13 |
14 | Input.propTypes = {};
15 |
16 | export default Input;
17 |
--------------------------------------------------------------------------------
/server/socket/init/authorizationInit.js:
--------------------------------------------------------------------------------
1 | const CONSTS = require('../constants');
2 | const authEvent = require('../events/authorizationEvents');
3 |
4 | module.exports = function (io, socket) {
5 | socket.on(CONSTS.AUTHORIZATION_CONSTS.C2S_AUTH_AUTHORIZATION, data => authEvent.authorization(io, socket, data));
6 | socket.on(CONSTS.AUTHORIZATION_CONSTS.C2S_AUTH_REGISTERPAGEURL, data => authEvent.registerPageUrl(io, socket, data));
7 | socket.on('disconnect', data => authEvent.disconnect(io, socket, data));
8 | }
--------------------------------------------------------------------------------
/client/src/pages/protected/Leads.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { useDispatch } from 'react-redux'
3 | import { setPageTitle } from '../../features/common/headerSlice'
4 | import Leads from '../../features/leads'
5 |
6 | function InternalPage(){
7 | const dispatch = useDispatch()
8 |
9 | useEffect(() => {
10 | dispatch(setPageTitle({ title : "Leads"}))
11 | }, [])
12 |
13 |
14 | return(
15 |
16 | )
17 | }
18 |
19 | export default InternalPage
--------------------------------------------------------------------------------
/client/src/pages/protected/Dashboard.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useDispatch } from "react-redux";
3 | import { setPageTitle } from "../../features/common/headerSlice";
4 | import Dashboard from "../../features/dashboard/index";
5 |
6 | function InternalPage() {
7 | const dispatch = useDispatch();
8 |
9 | useEffect(() => {
10 | dispatch(setPageTitle({ title: "Dashboard" }));
11 | }, []);
12 |
13 | return ;
14 | }
15 |
16 | export default InternalPage;
17 |
--------------------------------------------------------------------------------
/client/src/pages/Settings/Team.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { useDispatch } from 'react-redux'
3 | import { setPageTitle } from '../../features/common/headerSlice'
4 | import Team from '../../features/settings/team'
5 |
6 | function InternalPage(){
7 | const dispatch = useDispatch()
8 |
9 | useEffect(() => {
10 | dispatch(setPageTitle({ title : "Team Members"}))
11 | }, [])
12 |
13 |
14 | return(
15 |
16 | )
17 | }
18 |
19 | export default InternalPage
--------------------------------------------------------------------------------
/server/src/routes/index.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 |
3 | //init Routes
4 |
5 | const authRoutes = require("./authRoutes");
6 | const articleRoutes = require("./articleRoutes");
7 | const adminRoutes = require("./adminRoutes");
8 | // email routes //
9 |
10 | //routes container
11 | const router = express.Router();
12 |
13 | router.use("/auth", authRoutes);
14 | router.use("/article", articleRoutes);
15 | router.use("/admin", adminRoutes);
16 | // email routes con//
17 |
18 | module.exports = router;
19 |
--------------------------------------------------------------------------------
/client/src/utils/globalConstantUtil.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = Object.freeze({
3 | MODAL_BODY_TYPES : {
4 | USER_DETAIL : "USER_DETAIL",
5 | LEAD_ADD_NEW : "LEAD_ADD_NEW",
6 | CONFIRMATION : "CONFIRMATION",
7 | DEFAULT : "",
8 | },
9 |
10 | RIGHT_DRAWER_TYPES : {
11 | NOTIFICATION : "NOTIFICATION",
12 | CALENDAR_EVENTS : "CALENDAR_EVENTS",
13 | },
14 |
15 | CONFIRMATION_MODAL_CLOSE_TYPES : {
16 | LEAD_DELETE : "LEAD_DELETE",
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/server/.gitignore:
--------------------------------------------------------------------------------
1 | # Build and Release Folders
2 | bin-debug/
3 | bin-release/
4 | [Oo]bj/
5 | [Bb]in/
6 |
7 | # Other files and folders
8 | .settings/
9 |
10 | # Executables
11 | *.swf
12 | *.air
13 | *.ipa
14 | *.apk
15 |
16 | # Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties`
17 | # should NOT be excluded as they contain compiler settings and other important
18 | # information for Eclipse / Flash Builder.
19 |
20 | # ignore node_modules
21 | /node_modules
22 |
23 | # environment variables
24 |
--------------------------------------------------------------------------------
/client/src/pages/Settings/ChangePassword.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useDispatch } from "react-redux";
3 | import { setPageTitle } from "../../features/common/headerSlice";
4 | import ChangePassword from "../../features/settings/changepassword";
5 |
6 | function InternalPage() {
7 | const dispatch = useDispatch();
8 |
9 | useEffect(() => {
10 | dispatch(setPageTitle({ title: "Settings" }));
11 | }, [dispatch]);
12 |
13 | return ;
14 | }
15 |
16 | export default InternalPage;
17 |
--------------------------------------------------------------------------------
/client/src/pages/protected/Integration.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { useDispatch } from 'react-redux'
3 | import { setPageTitle } from '../../features/common/headerSlice'
4 | import Integration from '../../features/integration'
5 |
6 | function InternalPage(){
7 |
8 | const dispatch = useDispatch()
9 |
10 | useEffect(() => {
11 | dispatch(setPageTitle({ title : "Integrations"}))
12 | }, [])
13 |
14 | return(
15 |
16 | )
17 | }
18 |
19 | export default InternalPage
--------------------------------------------------------------------------------
/client/src/pages/protected/Transactions.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { useDispatch } from 'react-redux'
3 | import { setPageTitle } from '../../features/common/headerSlice'
4 | import Transactions from '../../features/transactions'
5 |
6 | function InternalPage(){
7 | const dispatch = useDispatch()
8 |
9 | useEffect(() => {
10 | dispatch(setPageTitle({ title : "Transactions"}))
11 | }, [])
12 |
13 |
14 | return(
15 |
16 | )
17 | }
18 |
19 | export default InternalPage
--------------------------------------------------------------------------------
/server/src/configs/errorHandler.js:
--------------------------------------------------------------------------------
1 | // 404 - not found error handler
2 | exports.notFoundRoute = (req, res, next) => {
3 | res.status(404).json({ message: "Sorry! Your request page was not found." });
4 | };
5 |
6 | exports.errorHandler = (err, req, res, next) => {
7 | if (res.headersSent) {
8 | return next("Something went wrong.");
9 | } else {
10 | if (err.message) {
11 | res.status(500).json({ message: err.message });
12 | } else {
13 | res.status(500).json({ message: "There was an error." });
14 | }
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/client/src/pages/Component/Input.jsx:
--------------------------------------------------------------------------------
1 | export const Input = (prop) => {
2 | return (
3 |
4 |
13 |
14 | );
15 | };
16 |
--------------------------------------------------------------------------------
/client/src/redux/init.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | const initializeApp = () => {
4 | // Setting base URL for all API request via axios
5 | axios.defaults.baseURL = process.env.REACT_APP_BASE_URL;
6 |
7 | if (!process.env.NODE_ENV || process.env.NODE_ENV === "development") {
8 | // dev code
9 | } else {
10 | // Prod build code
11 |
12 | // Removing console.log from prod
13 | console.log = () => {};
14 |
15 | // init analytics here
16 | }
17 | };
18 |
19 | export default initializeApp;
20 |
--------------------------------------------------------------------------------
/client/src/features/common/components/NotificationBodyRightDrawer.js:
--------------------------------------------------------------------------------
1 | import { useSelector } from "react-redux";
2 |
3 | function NotificationBodyRightDrawer() {
4 | const state = useSelector((state) => state.article);
5 | console.log(state);
6 | return (
7 | <>
8 | {[""].map((v, i) => {
9 | return (
10 |
11 | {v}
12 |
13 | );
14 | })}
15 | >
16 | );
17 | }
18 |
19 | export default NotificationBodyRightDrawer;
20 |
--------------------------------------------------------------------------------
/server/socket/init/loggedusersInit.js:
--------------------------------------------------------------------------------
1 | const CONSTS = require('../constants');
2 | const loggedusersEvent = require('../events/loggedusersEvent');
3 |
4 | module.exports = function (io, socket) {
5 | socket.on(CONSTS.LOGGEDUSERS_CONSTS.C2S_LOGGEDUSERS_DISCONNECT, data => loggedusersEvent.disconnect(io, socket, data));
6 | socket.on(CONSTS.LOGGEDUSERS_CONSTS.C2S_LOGGEDUSERS_CONNECT, data => loggedusersEvent.authorization(io, socket, data));
7 | socket.on(CONSTS.LOGGEDUSERS_CONSTS.C2S_LOGGEDUSERS_REGISTER_ACTION, data => loggedusersEvent.registerAction(io, socket, data));
8 | }
--------------------------------------------------------------------------------
/client/src/components/Input/SearchBar.js:
--------------------------------------------------------------------------------
1 | function SearchBar({ searchText, onChange, className }) {
2 | return (
3 |
14 | );
15 | }
16 |
17 | export default SearchBar;
18 |
--------------------------------------------------------------------------------
/client/src/pages/Admin/UserManage/EditAccount/index.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useDispatch } from "react-redux";
3 | import { setPageTitle } from "../../../../features/common/headerSlice";
4 | import EditManage from "./EditManage";
5 |
6 | function EditAccount() {
7 | const dispatch = useDispatch();
8 | useEffect(() => {
9 | dispatch(setPageTitle({ title: "Edit Account" }));
10 | }, [dispatch]);
11 | return (
12 | <>
13 | User Page
14 |
15 | >
16 | );
17 | }
18 | export default EditAccount;
19 |
--------------------------------------------------------------------------------
/server/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["prettier", "airbnb-base"],
3 | "parserOptions": {
4 | "ecmaVersion": 12
5 | },
6 | "env": {
7 | "commonjs": true,
8 | "node": true
9 | },
10 | "rules": {
11 | "no-console": 0,
12 | "indent": 0,
13 | "linebreak-style": 0,
14 | "prettier/prettier": [
15 | "error",
16 | {
17 | "trailingComma": "es6",
18 | "singleQuote": false,
19 | "printWidth": 100,
20 | "tabWidth": 2,
21 | "semi": true
22 | }
23 | ]
24 | },
25 | "plugins": ["prettier"]
26 | }
27 |
--------------------------------------------------------------------------------
/server/socket/init/notificationInit.js:
--------------------------------------------------------------------------------
1 | const CONSTS = require('../constants');
2 | const notificationEvent = require('../events/notificationEvents');
3 |
4 | module.exports = function (io, socket) {
5 | socket.on(CONSTS.NOTIFICATION_CONSTS.C2S_NOTIFICATION_CREATE, data => notificationEvent.createNotification(io, socket, data));
6 | socket.on(CONSTS.NOTIFICATION_CONSTS.C2S_NOTIFICATION_READ, data => notificationEvent.readNotification(io, socket, data));
7 | socket.on(CONSTS.NOTIFICATION_CONSTS.C2S_NOTIFICATION_READ_BY_OBJID, data => notificationEvent.readNotificationByObjIds(io, socket, data));
8 | }
--------------------------------------------------------------------------------
/client/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "DashWind",
3 | "name": "DashWind",
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/src/components/Logo.jsx:
--------------------------------------------------------------------------------
1 | function Logo() {
2 | return (
3 |
4 |
5 |
6 |
Blog Website
7 |

12 |
13 |
14 |
15 | );
16 | }
17 |
18 | export default Logo;
19 |
--------------------------------------------------------------------------------
/client/src/components/CalendarView/util.js:
--------------------------------------------------------------------------------
1 | const moment = require("moment");
2 |
3 | module.exports = Object.freeze({
4 | CALENDAR_EVENT_STYLE : {
5 | "BLUE" : "bg-blue-200 dark:bg-blue-600 dark:text-blue-100",
6 | "GREEN" : "bg-green-200 dark:bg-green-600 dark:text-green-100",
7 | "PURPLE" : "bg-purple-200 dark:bg-purple-600 dark:text-purple-100",
8 | "ORANGE" : "bg-orange-200 dark:bg-orange-600 dark:text-orange-100",
9 | "PINK" : "bg-pink-200 dark:bg-pink-600 dark:text-pink-100",
10 | "MORE" : "hover:underline cursor-pointer font-medium "
11 | }
12 |
13 |
14 | });
15 |
--------------------------------------------------------------------------------
/client/src/pages/Settings/ProfileSettings.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useDispatch } from "react-redux";
3 | import { setPageTitle } from "../../features/common/headerSlice";
4 | import ProfileSettings from "../../features/settings/profilesettings";
5 | import { getAllCategory } from "../../redux/adminSlice";
6 |
7 | function InternalPage() {
8 | const dispatch = useDispatch();
9 |
10 | useEffect(() => {
11 | dispatch(setPageTitle({ title: "Settings" }));
12 | dispatch(getAllCategory())
13 | }, [dispatch]);
14 |
15 | return ;
16 | }
17 |
18 | export default InternalPage;
19 |
--------------------------------------------------------------------------------
/client/src/pages/Admin/UserManage/index.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 | import { useDispatch } from "react-redux";
3 | import UserManagePanel from "./UserManagePanel";
4 | import { allUser } from "../../../redux/adminSlice";
5 |
6 | function UserManage() {
7 | const [sortIndex, setSortIndex] = useState("createAt");
8 |
9 | const dispatch = useDispatch();
10 | useEffect(() => {
11 | dispatch(allUser(sortIndex));
12 | }, [dispatch, sortIndex]);
13 | return (
14 | <>
15 | User Page
16 |
17 | >
18 | );
19 | }
20 | export default UserManage;
21 |
--------------------------------------------------------------------------------
/server/src/routes/adminRoutes/categoryRoutes.js:
--------------------------------------------------------------------------------
1 | const router = require("express").Router();
2 |
3 | // init middleware
4 |
5 | // init controller
6 | const categoryCtrl = require("../../controllers/adminControllers/categoryController");
7 |
8 | const passport = require("passport");
9 | const middleware = passport.authenticate("jwt", { session: false });
10 |
11 | // Admin - Category
12 | router.post("/", middleware, categoryCtrl.createCategory);
13 | router.put("/:id", middleware, categoryCtrl.updateCategory);
14 | router.delete("/:id", middleware, categoryCtrl.deleteCategory);
15 | router.get("/all", middleware, categoryCtrl.getAllCategory);
16 |
17 | module.exports = router;
18 |
--------------------------------------------------------------------------------
/client/src/utils/requestServer.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | export const requestServer = async(type, url, data)=>{
3 | switch (type) {
4 | case 'post':
5 | const resPost = await axios.post(process.env.REACT_APP_BASE_URL + url, data)
6 | return resPost
7 | case 'get':
8 | const resGet = await axios.get(process.env.REACT_APP_BASE_URL + url)
9 | return resGet
10 | case 'delete':
11 | const resDel = await axios.delete(process.env.REACT_APP_BASE_URL + url)
12 | return resDel
13 | case 'put':
14 | const resPut = await axios.put(process.env.REACT_APP_BASE_URL + url, data)
15 | return resPut
16 | default:
17 | break;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/client/src/components/Input/StanSearchBar.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | import React, { useEffect } from 'react'
4 |
5 | function StanSearchBar({searchText, styleClass, placeholderText, setSearchText}) {
6 |
7 |
8 |
9 | const updateSearchInput = (value) => {
10 | setSearchText(value)
11 | }
12 |
13 | return (
14 |
15 |
16 | updateSearchInput(e.target.value)} className="input input-sm input-bordered w-full max-w-xs" />
17 |
18 |
19 | )
20 | }
21 |
22 | export default StanSearchBar
23 |
--------------------------------------------------------------------------------
/client/src/components/Input/SelectBoxSmall.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | function SelectBoxSmall(props) {
4 | const { options } = props;
5 | return (
6 |
7 |
23 |
24 | );
25 | }
26 |
27 | export default SelectBoxSmall;
28 |
--------------------------------------------------------------------------------
/server/src/db/connect.js:
--------------------------------------------------------------------------------
1 | // external modules import
2 | const mongoose = require('mongoose');
3 |
4 | const connectionString = 'mongodb://localhost:27017/team231';
5 |
6 | const connectDatabase = async () => {
7 | try {
8 | await mongoose
9 | .connect(connectionString, {
10 | useNewUrlParser: true,
11 | useUnifiedTopology: true,
12 | })
13 | .then(() => {
14 | console.log('Connected to MongoDB database successfully.');
15 | })
16 | .catch(error => {
17 | console.log('Error connecting to MongoDB: ', error.message);
18 | });
19 | } catch (error) {
20 | console.log('Database connection error: ', error.message);
21 | }
22 | };
23 |
24 | module.exports = connectDatabase;
25 |
--------------------------------------------------------------------------------
/client/src/components/Input/SelectBoxBig.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | function SelectBoxBig(props) {
4 | const { options } = props;
5 | return (
6 |
7 |
8 |
22 |
23 | );
24 | }
25 |
26 | export default SelectBoxBig;
27 |
--------------------------------------------------------------------------------
/server/src/configs/passport.js:
--------------------------------------------------------------------------------
1 | const JwtStrategy = require("passport-jwt").Strategy;
2 | const ExtractJwt = require("passport-jwt").ExtractJwt;
3 | const User = require("../models/userModel");
4 | const config = require("./config");
5 | const opts = {};
6 | opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
7 | opts.secretOrKey = config.secretOrKey;
8 |
9 | module.exports = (passport) => {
10 | passport.use(
11 | new JwtStrategy(opts, (jwt_payload, done) => {
12 | User.findOne({ username: jwt_payload.username })
13 | .then((user) => {
14 | if (user) return done(null, user);
15 | return done(null, false);
16 | })
17 | .catch((err) => {
18 | console.log(err);
19 | });
20 | })
21 | );
22 | };
23 |
--------------------------------------------------------------------------------
/client/src/redux/store.js:
--------------------------------------------------------------------------------
1 | import { configureStore } from "@reduxjs/toolkit";
2 | import headerSlice from "../features/common/headerSlice";
3 | import modalSlice from "../features/common/modalSlice";
4 | import rightDrawerSlice from "../features/common/rightDrawerSlice";
5 | import leadsSlice from "../features/leads/leadSlice";
6 | import authSlice from "./authSlice";
7 | import adminSlice from "./adminSlice";
8 | import articleSlice from "./articleSlice";
9 |
10 | const combinedReducer = {
11 | header: headerSlice,
12 | rightDrawer: rightDrawerSlice,
13 | modal: modalSlice,
14 | lead: leadsSlice,
15 | auth: authSlice,
16 | admin: adminSlice,
17 | article: articleSlice,
18 | };
19 |
20 | export default configureStore({
21 | reducer: combinedReducer,
22 | });
23 |
--------------------------------------------------------------------------------
/server/src/configs/socket.js:
--------------------------------------------------------------------------------
1 | const io = require("socket.io");
2 | const User = require("../models/userModel");
3 | // const { setSocketMsg } = require("../../../client/src/redux/articleSlice");
4 |
5 | let userarr = [];
6 |
7 | exports.initialize = (server) => {
8 | const sio = io.listen(server);
9 | console.log("socket server is running");
10 | sio.on("connection", function (socket) {
11 | socket.emit("connection-success", "successfully connected to socket server");
12 | socket.on("createArticle", function (msg) {
13 | socket.emit("createArticle-resend", msg);
14 | });
15 | socket.on("signin", function (username, msg) {
16 | socket.username = username;
17 | userarr.push(socket);
18 | socket.to(username).emit("signin-resend", msg);
19 | });
20 | });
21 | };
22 |
--------------------------------------------------------------------------------
/client/src/components/Input/TextAreaInput.js:
--------------------------------------------------------------------------------
1 |
2 | function TextAreaInput({ labelTitle, name, value, onChange, placeholder, containerStyle, labelStyle, className }) {
3 | return (
4 |
5 |
8 |
15 |
16 | );
17 | }
18 |
19 | export default TextAreaInput;
20 |
--------------------------------------------------------------------------------
/client/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
40 | .signHead {
41 | text-shadow: 5px 5px 2px gray;
42 | color: lightgray;
43 | }
--------------------------------------------------------------------------------
/server/socket/index.js:
--------------------------------------------------------------------------------
1 | const CONSTS = require('./constants');
2 | const chatUtils = require('./utils/chatUtils');
3 | const authUtils = require('./utils/authorizationUtils');
4 | const chatInit = require('./init/chatInit');
5 | const authInit = require('./init/authorizationInit');
6 | const loggedusersInit = require('./init/loggedusersInit');
7 | const notificationInit = require('./init/notificationInit');
8 |
9 | exports = module.exports = function (io) {
10 | // Set socket.io listeners.
11 | setInterval(() => {
12 | authUtils.emitUserlistInfos(io);
13 | }, 60 * 1000); // 1min
14 |
15 | io.of(CONSTS.SOCKET).on('connection', (socket) => {
16 |
17 | authInit(io, socket);
18 |
19 |
20 | chatInit(io, socket);
21 |
22 |
23 | loggedusersInit(io, socket);
24 |
25 |
26 | notificationInit(io, socket);
27 | });
28 | };
29 |
--------------------------------------------------------------------------------
/client/src/components/Input/InputText.js:
--------------------------------------------------------------------------------
1 | function InputText({
2 | name,
3 | labelTitle,
4 | labelStyle,
5 | type,
6 | containerStyle,
7 | placeholder,
8 | value,
9 | setHandleKeyDown,
10 | onChange,
11 | }) {
12 | return (
13 |
14 |
17 |
26 |
27 | );
28 | }
29 |
30 | export default InputText;
31 |
--------------------------------------------------------------------------------
/client/src/components/Cards/TitleCard.js:
--------------------------------------------------------------------------------
1 | import Subtitle from "../Typography/Subtitle";
2 |
3 | function TitleCard({ title, children, topMargin, TopSideButtons }) {
4 | return (
5 |
6 | {/* Title for Card */}
7 |
8 | {title}
9 |
10 | {/* Top side button, show only if present */}
11 | {TopSideButtons && {TopSideButtons}
}
12 |
13 |
14 |
15 |
16 | {/** Card Body */}
17 |
{children}
18 |
19 | );
20 | }
21 |
22 | export default TitleCard;
23 |
--------------------------------------------------------------------------------
/client/src/components/Input/Inputdisabled.js:
--------------------------------------------------------------------------------
1 | function InputDisabled({
2 | name,
3 | labelTitle,
4 | labelStyle,
5 | type,
6 | value,
7 | containerStyle,
8 | placeholder,
9 | setHandleKeyDown,
10 | onChange,
11 | }) {
12 | return (
13 |
14 |
19 |
29 |
30 | );
31 | }
32 |
33 | export default InputDisabled;
34 |
--------------------------------------------------------------------------------
/client/src/features/user/components/TemplatePointers.js:
--------------------------------------------------------------------------------
1 | function TemplatePointers(){
2 | return(
3 | <>
4 | Admin Dashboard Starter Kit
5 | ✓ Light/dark mode toggle
6 | ✓ Redux toolkit and other utility libraries configured
7 | ✓ Calendar, Modal, Sidebar components
8 | ✓ User-friendly documentation
9 | ✓ Daisy UI components, Tailwind CSS support
10 | >
11 | )
12 | }
13 |
14 | export default TemplatePointers
--------------------------------------------------------------------------------
/client/src/pages/protected/404.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { useDispatch } from 'react-redux'
3 | import { setPageTitle } from '../../features/common/headerSlice'
4 | import FaceFrownIcon from '@heroicons/react/24/solid/FaceFrownIcon'
5 |
6 | function InternalPage(){
7 |
8 | const dispatch = useDispatch()
9 |
10 | useEffect(() => {
11 | dispatch(setPageTitle({ title : ""}))
12 | }, [])
13 |
14 | return(
15 |
16 |
17 |
18 |
19 |
404 - Not Found
20 |
21 |
22 |
23 | )
24 | }
25 |
26 | export default InternalPage
--------------------------------------------------------------------------------
/client/src/components/Input/BigInputText.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | function BigInputText({
4 | name,
5 | labelTitle,
6 | labelStyle,
7 | type,
8 | value,
9 | containerStyle,
10 | placeholder,
11 | setHandleKeyDown,
12 | onChange,
13 | }) {
14 | return (
15 |
16 |
21 |
30 |
31 | );
32 | }
33 |
34 | export default BigInputText;
35 |
--------------------------------------------------------------------------------
/client/src/pages/protected/Welcome.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { useDispatch } from 'react-redux'
3 | import { setPageTitle } from '../../features/common/headerSlice'
4 | import {Link} from 'react-router-dom'
5 | import TemplatePointers from '../../features/user/components/TemplatePointers'
6 |
7 | function InternalPage(){
8 |
9 | const dispatch = useDispatch()
10 |
11 | useEffect(() => {
12 | dispatch(setPageTitle({ title : ""}))
13 | }, [])
14 |
15 | return(
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | )
25 | }
26 |
27 | export default InternalPage
--------------------------------------------------------------------------------
/client/src/index.js:
--------------------------------------------------------------------------------
1 | import React, { Suspense } from "react";
2 | import ReactDOM from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App";
5 | import reportWebVitals from "./reportWebVitals";
6 | import store from "./redux/store";
7 | import { Provider } from "react-redux";
8 | import SuspenseContent from "./containers/SuspenseContent";
9 |
10 | const root = ReactDOM.createRoot(document.getElementById("root"));
11 | root.render(
12 | }>
13 |
14 |
15 |
16 |
17 |
18 |
19 | );
20 |
21 | // If you want to start measuring performance in your app, pass a function
22 | // to log results (for example: reportWebVitals(console.log))
23 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
24 | reportWebVitals();
25 |
--------------------------------------------------------------------------------
/client/src/pages/protected/Blank.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { useDispatch } from 'react-redux'
3 | import { setPageTitle } from '../../features/common/headerSlice'
4 |
5 | import DocumentIcon from '@heroicons/react/24/solid/DocumentIcon'
6 |
7 | function InternalPage(){
8 |
9 | const dispatch = useDispatch()
10 |
11 | useEffect(() => {
12 | dispatch(setPageTitle({ title : "Page Title"}))
13 | }, [])
14 |
15 | return(
16 |
17 |
18 |
19 |
20 |
Blank Page
21 |
22 |
23 |
24 | )
25 | }
26 |
27 | export default InternalPage
--------------------------------------------------------------------------------
/client/src/features/dashboard/components/AmountStats.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | function AmountStats() {
4 | return (
5 |
6 |
7 |
Amount to be Collected
8 |
$25,600
9 |
10 |
11 |
12 |
13 |
14 |
Cash in hand
15 |
$5,600
16 |
17 |
18 |
19 |
20 |
21 | )
22 | }
23 |
24 | export default AmountStats
--------------------------------------------------------------------------------
/client/src/features/user/LandingIntro.js:
--------------------------------------------------------------------------------
1 | import TemplatePointers from "./components/TemplatePointers"
2 |
3 |
4 |
5 | function LandingIntro(){
6 |
7 | return(
8 |
9 |
10 |
11 |
12 |
DashWind
13 |
14 |
15 |
16 | {/* Importing pointers component */}
17 |
18 |
19 |
20 |
21 |
22 |
23 | )
24 |
25 | }
26 |
27 | export default LandingIntro
--------------------------------------------------------------------------------
/client/src/components/Input/ToogleInput.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | function ToogleInput({
4 | labelTitle,
5 | labelStyle,
6 | type,
7 | containerStyle,
8 | defaultValue,
9 | placeholder,
10 | updateFormValue,
11 | updateType,
12 | }) {
13 | const [value, setValue] = useState(defaultValue);
14 |
15 | const updateToogleValue = () => {
16 | setValue(!value);
17 | updateFormValue({ updateType, value: !value });
18 | };
19 |
20 | return (
21 |
22 |
26 |
27 | );
28 | }
29 |
30 | export default ToogleInput;
31 |
--------------------------------------------------------------------------------
/server/socketApp.js:
--------------------------------------------------------------------------------
1 | const socketIo = require('socket.io');
2 |
3 | var io;
4 | var nameList = {}
5 |
6 | exports.socketServer = (server) => {
7 | io = socketIo.listen(server)
8 | io.sockets.on('connection', function (socket) {
9 | console.log('socket is joined');
10 | socket.on('id', function (data) {
11 | nameList[data.userId] = socket
12 | socket.on('like',function (data) {
13 | for (const key in nameList) {
14 | if (key === data.data.author) {
15 | nameList[key].emit('like', {msg: data.data.msg})
16 | }
17 | }
18 | })
19 | socket.on('comment', function (data) {
20 | for (const key in nameList) {
21 | if (key === data.data.author) {
22 | nameList[key].emit('comment', {msg: data.data.msg})
23 | }
24 | }
25 | })
26 | })
27 | socket.on('disconnect',function () {
28 | console.log('socket is disconnected');
29 | })
30 | })
31 | }
--------------------------------------------------------------------------------
/client/src/utils/InputText.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 |
4 | function InputText({ labelTitle, lableStyle, type, containerStyle, defaultValue, updateFormValue, updateType, autoFocus, placeholder}) {
5 | const [value, setValue] = useState(defaultValue);
6 |
7 | const updateInputValue = (val) => {
8 | setValue(val);
9 | updateFormValue({ updateType, value: val});
10 | }
11 |
12 | return (
13 |
14 |
17 | updateInputValue(e.target.value)}
19 | className="h-[50px] rounded-lg px-[20px] w-full outline-none " />
20 |
21 | );
22 | }
23 |
24 | export default InputText;
--------------------------------------------------------------------------------
/server/src/routes/adminRoutes/userRoutes.js:
--------------------------------------------------------------------------------
1 | const router = require("express").Router();
2 |
3 | // init middleware
4 | const { requireAdmin } = require("../../middleware/authorization");
5 |
6 | // init controller
7 | const userManageCtrl = require("../../controllers/adminControllers/userController");
8 |
9 | const passport = require("passport");
10 | const middleware = passport.authenticate("jwt", { session: false });
11 |
12 | // Admin - Users
13 | router.get("/all", userManageCtrl.allUser);
14 | router.put("/role/:id", middleware, userManageCtrl.permissionUser);
15 | router.delete("/:id", middleware, userManageCtrl.delUser);
16 |
17 | // User - Profile
18 | router.get("/:id", userManageCtrl.getUser);
19 | router.put("/:id", middleware, userManageCtrl.changeInfo);
20 | router.put("/password/:id", middleware, userManageCtrl.changePassword);
21 | router.put("/avatar/:id", userManageCtrl.changeAvatar);
22 | router.put("/follow/:id", userManageCtrl.addFollower);
23 |
24 | module.exports = router;
25 |
--------------------------------------------------------------------------------
/client/src/pages/Admin/Category/index.jsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-hooks/exhaustive-deps */
2 | import { useEffect, useState } from "react";
3 | import { useDispatch, useSelector } from "react-redux";
4 | import InputCategory from "./components/InputCategory";
5 | import TableCategory from "./components/TableCategory";
6 | import { getAllCategory } from "../../../redux/adminSlice";
7 |
8 | function CategoryPage() {
9 | const [sortIndex, setSortIndex] = useState("createAt");
10 | const [searchVal, setSearchVal] = useState("");
11 | const dispatch = useDispatch();
12 | const { flag } = useSelector((state) => state.admin);
13 |
14 | useEffect(() => {
15 | dispatch(getAllCategory({sortIndex, searchVal}));
16 | }, [flag, sortIndex, searchVal]);
17 | return (
18 | <>
19 | Category Page
20 |
21 |
22 | >
23 | );
24 | }
25 | export default CategoryPage;
26 |
--------------------------------------------------------------------------------
/server/src/routes/articleRoutes.js:
--------------------------------------------------------------------------------
1 | const router = require("express").Router();
2 |
3 | // init controller
4 | const articleCtrl = require("../controllers/articleController");
5 |
6 | const passport = require("passport");
7 | const middleware = passport.authenticate("jwt", { session: false });
8 |
9 | // routers //
10 | router.post("/create", middleware, articleCtrl.createArticle);
11 | router.put("/:id", middleware, articleCtrl.updateArticle);
12 | router.delete("/:id", middleware, articleCtrl.deleteArticle);
13 | router.get("/home", articleCtrl.getHomeArticles);
14 | router.get("/all", articleCtrl.getAllArticles);
15 | router.get("/my", articleCtrl.getMyArticles);
16 | router.get("/favorite", articleCtrl.getFavoriteArticles);
17 | router.get("/draft", articleCtrl.getDraftArticles);
18 | router.get("/:id", middleware, articleCtrl.getAArticle);
19 | router.put("/comment/:id", middleware, articleCtrl.addComment);
20 | router.put("/favorite/:id", middleware, articleCtrl.addFavorite);
21 |
22 | module.exports = router;
23 |
--------------------------------------------------------------------------------
/client/src/features/dashboard/components/PageStats.js:
--------------------------------------------------------------------------------
1 | import HeartIcon from '@heroicons/react/24/outline/HeartIcon'
2 | import BoltIcon from '@heroicons/react/24/outline/BoltIcon'
3 |
4 |
5 | function PageStats({}){
6 | return(
7 |
8 |
9 |
10 |
11 |
12 |
13 |
Total Likes
14 |
25.6K
15 |
21% more than last month
16 |
17 |
18 |
19 |
20 |
21 |
22 |
Page Views
23 |
2.6M
24 |
14% more than last month
25 |
26 |
27 | )
28 | }
29 |
30 | export default PageStats
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/client/src/features/dashboard/components/DashboardStats.js:
--------------------------------------------------------------------------------
1 | function DashboardStats({title, icon, value, description, colorIndex}){
2 |
3 | const COLORS = ["primary", "primary"]
4 |
5 | const getDescStyle = () => {
6 | if(description.includes("↗︎"))return "font-bold text-green-700 dark:text-green-300"
7 | else if(description.includes("↙"))return "font-bold text-rose-500 dark:text-red-400"
8 | else return ""
9 | }
10 |
11 | return(
12 |
13 |
14 |
{icon}
15 |
{title}
16 |
{value}
17 |
{description}
18 |
19 |
20 | )
21 | }
22 |
23 | export default DashboardStats
--------------------------------------------------------------------------------
/client/src/features/common/rightDrawerSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from "@reduxjs/toolkit";
2 |
3 | export const rightDrawerSlice = createSlice({
4 | name: "rightDrawer",
5 | initialState: {
6 | header: "", // current title state management
7 | isOpen: false, // right drawer state management for opening closing
8 | bodyType: "", // right drawer content management
9 | extraObject: {},
10 | },
11 | reducers: {
12 | openRightDrawer: (state, action) => {
13 | const { header, bodyType, extraObject } = action.payload;
14 | state.isOpen = true;
15 | state.bodyType = bodyType;
16 | state.header = header;
17 | state.extraObject = extraObject;
18 | },
19 |
20 | closeRightDrawer: (state, action) => {
21 | state.isOpen = false;
22 | state.bodyType = "";
23 | state.header = "";
24 | state.extraObject = {};
25 | },
26 | },
27 | });
28 |
29 | export const { openRightDrawer, closeRightDrawer } = rightDrawerSlice.actions;
30 |
31 | export default rightDrawerSlice.reducer;
32 |
--------------------------------------------------------------------------------
/API Design.md:
--------------------------------------------------------------------------------
1 | # API Design
2 |
3 | ## Auth
4 |
5 | post: /api/signup
6 |
7 | post: /api/signin
8 |
9 | ## Articles
10 |
11 | post /api/article/create
12 |
13 | put /api/article/:id
14 |
15 | delete /api/article/:id
16 |
17 | get /api/article/home
18 |
19 | get /api/article/:id
20 |
21 | put /api/article/comment/:id
22 |
23 | put /api/article/favorite/:id
24 |
25 | - sort by latest
26 | - sort by cnt of comments
27 | - sort by cnt of favourites
28 | - search by title
29 |
30 | ## Admin
31 |
32 | ### User
33 |
34 | - Manage
35 |
36 | get /api/admin/user/all
37 |
38 | put /api/admin/user/role/:id
39 |
40 | delete /api/admin/user/:id
41 |
42 | - Profile
43 |
44 | get /api/admin/user/:id
45 |
46 | put /api/admin/user/:id
47 |
48 | put /api/admin/user/password/:id
49 |
50 | put /api/admin/user/avatar/:id
51 |
52 | ### Category
53 |
54 | post /api/admin/category/
55 |
56 | put /api/admin/category/:id
57 |
58 | delete /api/admin/category/:id
59 |
60 | get /api/admin/category/all
61 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Finalgoal231
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/client/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Dashwind
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/client/src/features/common/headerSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit'
2 |
3 | export const headerSlice = createSlice({
4 | name: 'header',
5 | initialState: {
6 | pageTitle: "Home", // current page title state management
7 | noOfNotifications : 5, // no of unread notifications
8 | newNotificationMessage : "", // message of notification to be shown
9 | newNotificationStatus : 1, // to check the notification type - success/ error/ info
10 | },
11 | reducers: {
12 | setPageTitle: (state, action) => {
13 | state.pageTitle = action.payload.title
14 | },
15 | removeNotificationMessage: (state, action) => {
16 | state.newNotificationMessage = ""
17 | },
18 |
19 | showNotification: (state, action) => {
20 | state.newNotificationMessage = action.payload.message
21 | state.newNotificationStatus = action.payload.status
22 | },
23 | }
24 | })
25 |
26 | export const { setPageTitle, removeNotificationMessage, showNotification } = headerSlice.actions
27 |
28 | export default headerSlice.reducer
--------------------------------------------------------------------------------
/client/src/features/common/modalSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from "@reduxjs/toolkit";
2 |
3 | export const modalSlice = createSlice({
4 | name: "modal",
5 | initialState: {
6 | title: "", // current title state management
7 | isOpen: false, // modal state management for opening closing
8 | bodyType: "", // modal content management
9 | size: "", // modal content management
10 | extraObject: {},
11 | },
12 | reducers: {
13 | openModal: (state, action) => {
14 | const { title, bodyType, extraObject, size } = action.payload;
15 | state.isOpen = true;
16 | state.bodyType = bodyType;
17 | state.title = title;
18 | state.size = size || "md";
19 | state.extraObject = extraObject;
20 | },
21 |
22 | closeModal: (state, action) => {
23 | state.isOpen = false;
24 | state.bodyType = "";
25 | state.title = "";
26 | state.extraObject = {};
27 | },
28 | },
29 | });
30 |
31 | export const { openModal, closeModal } = modalSlice.actions;
32 |
33 | export default modalSlice.reducer;
34 |
--------------------------------------------------------------------------------
/server/src/models/articleModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 |
3 | const articleSchema = new mongoose.Schema({
4 | from: {
5 | type: mongoose.Schema.ObjectId,
6 | ref: "User",
7 | required: true,
8 | },
9 | title: {
10 | type: String,
11 | require: true,
12 | },
13 | content: {
14 | type: String,
15 | required: true,
16 | },
17 | favorite: [
18 | {
19 | user: {
20 | type: mongoose.Schema.ObjectId,
21 | ref: "User",
22 | required: true,
23 | },
24 | },
25 | ],
26 | category: { type: String },
27 | tags: [
28 | {
29 | type: String,
30 | },
31 | ],
32 | delected: {
33 | type: Date,
34 | default: null,
35 | },
36 | complete: {
37 | type: Boolean,
38 | default: false,
39 | },
40 | parent: { type: mongoose.Schema.ObjectId, ref: "Article", default: null },
41 | comment: [
42 | {
43 | ans: {
44 | type: mongoose.Schema.ObjectId,
45 | ref: "Article",
46 | required: true,
47 | },
48 | },
49 | ],
50 | createdAt: {
51 | type: Date,
52 | default: Date.now,
53 | },
54 | });
55 |
56 | module.exports = mongoose.model("Article", articleSchema);
57 |
--------------------------------------------------------------------------------
/client/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | .loading-indicator:before {
6 | content: '';
7 | background: #00000080;
8 | position: fixed;
9 | width: 100%;
10 | height: 100%;
11 | top: 0;
12 | left: 0;
13 | z-index: 1000;
14 | }
15 |
16 | .loading-indicator:after {
17 | content: ' ';
18 | position: fixed;
19 | top: 40%;
20 | left: 45%;
21 | z-index: 10010;
22 | color:white;
23 | text-align:center;
24 | font-weight:bold;
25 | font-size:1.2rem;
26 | border: 16px solid #f3f3f3; /* Light grey */
27 | border-top: 16px solid #0474bf; /* Blue */
28 | border-radius: 50%;
29 | width: 120px;
30 | height: 120px;
31 | animation: spin 2s linear infinite;
32 | }
33 |
34 | .bg-pic {
35 | background-image: url("../public/assets/images/background_outrun.png");
36 | height: 100vh;
37 | background-size: 100% 100%;
38 | }
39 |
40 | .passBtn{
41 | width: 5rem;
42 | height: 3.125rem;
43 | border-radius: 0.7rem;
44 | border: 1px solid rgb(238, 236, 127);
45 | animation: btnact 1s infinite;
46 | }
47 |
48 | @keyframes btnact {
49 | 0%{
50 |
51 | }
52 | 100%{
53 | transform: scale(1.5);
54 | opacity: 0;
55 | }
56 | }
--------------------------------------------------------------------------------
/client/src/features/leads/leadSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
2 | import axios from 'axios'
3 |
4 |
5 |
6 | export const getLeadsContent = createAsyncThunk('/leads/content', async () => {
7 | const response = await axios.get('/api/users?page=2', {})
8 | return response.data;
9 | })
10 |
11 | export const leadsSlice = createSlice({
12 | name: 'leads',
13 | initialState: {
14 | isLoading: false,
15 | leads : []
16 | },
17 | reducers: {
18 |
19 |
20 | addNewLead: (state, action) => {
21 | let {newLeadObj} = action.payload
22 | state.leads = [...state.leads, newLeadObj]
23 | },
24 |
25 | deleteLead: (state, action) => {
26 | let {index} = action.payload
27 | state.leads.splice(index, 1)
28 | }
29 | },
30 |
31 | extraReducers: {
32 | [getLeadsContent.pending]: state => {
33 | state.isLoading = true
34 | },
35 | [getLeadsContent.fulfilled]: (state, action) => {
36 | state.leads = action.payload.data
37 | state.isLoading = false
38 | },
39 | [getLeadsContent.rejected]: state => {
40 | state.isLoading = false
41 | },
42 | }
43 | })
44 |
45 | export const { addNewLead, deleteLead } = leadsSlice.actions
46 |
47 | export default leadsSlice.reducer
--------------------------------------------------------------------------------
/Model Design.md:
--------------------------------------------------------------------------------
1 | -Auth
2 |
3 | username:{
4 | type:string;
5 | required:true;
6 | unique: true;
7 | $regex:[a-z][0-9][_];
8 | },
9 | name:{
10 | type:string;
11 | required:true
12 | },
13 | bio:{
14 | type:string;
15 | },
16 | avatar:{
17 | type: string;/*avatar's path */
18 | defaule:"public/default.jpg";
19 | }
20 | role:{
21 | type:string;
22 | default:"guest";
23 | },
24 | follow:[{
25 | type:mongoose.Schema.ObjectId,
26 | }],
27 | category:[{
28 | type:mongoose.Schema.ObjectId,
29 | }]
30 |
31 | -Article
32 | from:{
33 | type:string;
34 | required:true;
35 | },
36 | title:{
37 | type:string;
38 | required:true;
39 | },
40 | category:{
41 | type:string;
42 | required:true
43 | },
44 | tags:[{
45 | type:string;
46 | }],
47 | content:{
48 | type: string;
49 | },
50 | draft:{
51 | type:number;
52 | default:0;
53 | },
54 | favouritor: [{
55 | type:String;
56 | default:0;
57 | }]a,
58 | comment:[{
59 | from:{
60 | type:string;
61 | required:true;
62 | },
63 | title:{
64 | type:string;
65 | required:true;
66 | },
67 | category:{
68 | type:string;
69 | required:true
70 | },
71 | tags:[{
72 | type:string;
73 | }],
74 | content:{
75 | type: string;
76 | },
77 |
78 | draft:{
79 | type:number;
80 | default:0;
81 | },
82 | }],
83 |
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/client/src/features/charts/components/LineChart.js:
--------------------------------------------------------------------------------
1 | import {
2 | Chart as ChartJS,
3 | CategoryScale,
4 | LinearScale,
5 | PointElement,
6 | LineElement,
7 | Title,
8 | Tooltip,
9 | Filler,
10 | Legend,
11 | } from 'chart.js';
12 | import { Line } from 'react-chartjs-2';
13 | import TitleCard from '../../../components/Cards/TitleCard';
14 |
15 | ChartJS.register(
16 | CategoryScale,
17 | LinearScale,
18 | PointElement,
19 | LineElement,
20 | Title,
21 | Tooltip,
22 | Filler,
23 | Legend
24 | );
25 |
26 | function LineChart(){
27 |
28 | const options = {
29 | responsive: true,
30 | plugins: {
31 | legend: {
32 | position: 'top',
33 | },
34 | },
35 | };
36 |
37 |
38 | const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
39 |
40 | const data = {
41 | labels,
42 | datasets: [
43 | {
44 | fill: true,
45 | label: 'MAU',
46 | data: labels.map(() => { return Math.random() * 100 + 500 }),
47 | borderColor: 'rgb(53, 162, 235)',
48 | backgroundColor: 'rgba(53, 162, 235, 0.5)',
49 | },
50 | ],
51 | };
52 |
53 |
54 | return(
55 |
56 |
57 |
58 | )
59 | }
60 |
61 |
62 | export default LineChart
--------------------------------------------------------------------------------
/client/src/features/dashboard/components/LineChart.js:
--------------------------------------------------------------------------------
1 | import {
2 | Chart as ChartJS,
3 | CategoryScale,
4 | LinearScale,
5 | PointElement,
6 | LineElement,
7 | Title,
8 | Tooltip,
9 | Filler,
10 | Legend,
11 | } from 'chart.js';
12 | import { Line } from 'react-chartjs-2';
13 | import TitleCard from '../../../components/Cards/TitleCard';
14 |
15 | ChartJS.register(
16 | CategoryScale,
17 | LinearScale,
18 | PointElement,
19 | LineElement,
20 | Title,
21 | Tooltip,
22 | Filler,
23 | Legend
24 | );
25 |
26 | function LineChart(){
27 |
28 | const options = {
29 | responsive: true,
30 | plugins: {
31 | legend: {
32 | position: 'top',
33 | },
34 | },
35 | };
36 |
37 |
38 | const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
39 |
40 | const data = {
41 | labels,
42 | datasets: [
43 | {
44 | fill: true,
45 | label: 'MAU',
46 | data: labels.map(() => { return Math.random() * 100 + 500 }),
47 | borderColor: 'rgb(53, 162, 235)',
48 | backgroundColor: 'rgba(53, 162, 235, 0.5)',
49 | },
50 | ],
51 | };
52 |
53 |
54 | return(
55 |
56 |
57 |
58 | )
59 | }
60 |
61 |
62 | export default LineChart
--------------------------------------------------------------------------------
/client/src/features/dashboard/components/BarChart.js:
--------------------------------------------------------------------------------
1 | import {
2 | Chart as ChartJS,
3 | CategoryScale,
4 | LinearScale,
5 | BarElement,
6 | Title,
7 | Tooltip,
8 | Legend,
9 | } from 'chart.js';
10 | import { Bar } from 'react-chartjs-2';
11 | import TitleCard from '../../../components/Cards/TitleCard';
12 |
13 | ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);
14 |
15 | function BarChart(){
16 |
17 | const options = {
18 | responsive: true,
19 | plugins: {
20 | legend: {
21 | position: 'top',
22 | }
23 | },
24 | };
25 |
26 | const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
27 |
28 | const data = {
29 | labels,
30 | datasets: [
31 | {
32 | label: 'Store 1',
33 | data: labels.map(() => { return Math.random() * 1000 + 500 }),
34 | backgroundColor: 'rgba(255, 99, 132, 1)',
35 | },
36 | {
37 | label: 'Store 2',
38 | data: labels.map(() => { return Math.random() * 1000 + 500 }),
39 | backgroundColor: 'rgba(53, 162, 235, 1)',
40 | },
41 | ],
42 | };
43 |
44 | return(
45 |
46 |
47 |
48 |
49 | )
50 | }
51 |
52 |
53 | export default BarChart
--------------------------------------------------------------------------------
/client/src/features/charts/components/BarChart.js:
--------------------------------------------------------------------------------
1 | import {
2 | Chart as ChartJS,
3 | CategoryScale,
4 | LinearScale,
5 | BarElement,
6 | Title,
7 | Tooltip,
8 | Legend,
9 | } from 'chart.js';
10 | import { Bar } from 'react-chartjs-2';
11 | import TitleCard from '../../../components/Cards/TitleCard';
12 |
13 | ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);
14 |
15 | function BarChart(){
16 |
17 | const options = {
18 | responsive: true,
19 | plugins: {
20 | legend: {
21 | position: 'top',
22 | }
23 | },
24 | };
25 |
26 | const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
27 |
28 | const data = {
29 | labels,
30 | datasets: [
31 | {
32 | label: 'Store 1',
33 | data: labels.map(() => { return Math.random() * 1000 + 500 }),
34 | backgroundColor: 'rgba(255, 99, 132, 1)',
35 | },
36 | {
37 | label: 'Store 2',
38 | data: labels.map(() => { return Math.random() * 1000 + 500 }),
39 | backgroundColor: 'rgba(53, 162, 235, 1)',
40 | },
41 | ],
42 | };
43 |
44 | return(
45 |
46 |
47 |
48 |
49 | )
50 | }
51 |
52 |
53 | export default BarChart
--------------------------------------------------------------------------------
/server/src/middleware/authorization.js:
--------------------------------------------------------------------------------
1 | const jwt = require("jsonwebtoken");
2 | const config = require("../configs/config");
3 |
4 | module.exports.requireUser = (req, res, next) => {
5 | const token = req.header("authorization").replace("bareer ", "");
6 |
7 | jwt.verify(token, config.secretOrKey, function (err, decoded) {
8 | if (err) res.status(419).json(err);
9 | else {
10 | req.user = decoded;
11 | next();
12 | }
13 | });
14 | };
15 |
16 | module.exports.requireSeller = (req, res, next) => {
17 | const token = req.header("authorization").replace("bareer ", "");
18 |
19 | jwt.verify(token, config.secretOrKey, function (err, decoded) {
20 | if (err) res.status(419).json(err);
21 | else {
22 | if (decoded.role === "seller" || decoded.role === "admin") {
23 | req.user = decoded;
24 | next();
25 | } else {
26 | res.status(419).json({ token: "Invalid Seller token!" });
27 | }
28 | }
29 | });
30 | };
31 |
32 | module.exports.requireAdmin = (req, res, next) => {
33 | const token = req.header("authorization").replace("bareer ", "");
34 | jwt.verify(token, config.secretOrKey, function (err, decoded) {
35 | if (err) res.status(419).json(err);
36 | else {
37 | if (decoded.role === "admin") {
38 | req.user = decoded;
39 | next();
40 | } else {
41 | res.status(419).json({ token: "Invalid Admin token!" });
42 | }
43 | }
44 | });
45 | };
46 |
--------------------------------------------------------------------------------
/client/src/features/common/components/ConfirmationModalBody.js:
--------------------------------------------------------------------------------
1 | import { useDispatch, useSelector } from "react-redux";
2 | import axios from "axios";
3 | import { CONFIRMATION_MODAL_CLOSE_TYPES, MODAL_CLOSE_TYPES } from "../../../utils/globalConstantUtil";
4 | import { deleteLead } from "../../leads/leadSlice";
5 | import { showNotification } from "../headerSlice";
6 |
7 | function ConfirmationModalBody({ extraObject, closeModal }) {
8 | const dispatch = useDispatch();
9 |
10 | const { message, type, _id, index } = extraObject;
11 |
12 | const proceedWithYes = async () => {
13 | if (type === CONFIRMATION_MODAL_CLOSE_TYPES.LEAD_DELETE) {
14 | // positive response, call api or dispatch redux function
15 | dispatch(deleteLead({ index }));
16 | dispatch(showNotification({ message: "Lead Deleted!", status: 1 }));
17 | }
18 | closeModal();
19 | };
20 |
21 | return (
22 | <>
23 | {message}
24 |
25 |
26 |
29 |
30 |
33 |
34 | >
35 | );
36 | }
37 |
38 | export default ConfirmationModalBody;
39 |
--------------------------------------------------------------------------------
/client/src/features/dashboard/components/Toolbar.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import SearchBar from "../.././../components/Input/SearchBar";
3 | import SelectBoxSmall from "../../../components/Input/SelectBoxSmall";
4 | import { Button } from "../../../components/Button";
5 | import { AiOutlinePlus } from "react-icons/ai";
6 |
7 | const Toolbar = (prop) => {
8 | return (
9 | <>
10 |
11 |
12 |
13 |
14 |
15 |
20 |
25 |
32 |
33 | >
34 | );
35 | };
36 |
37 | export default Toolbar;
38 |
--------------------------------------------------------------------------------
/server/socket/events/loggedusersEvent.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 | const CONSTS = require('../constants');
3 | const loggedusersUtils = require('../utils/loggedusersUtils');
4 |
5 | exports.authorization = (io, socket, data) => {
6 | const userId = data.userId;
7 | const userInfo = _.get( io, `connectedUsers.userlist.${userId}` );
8 | // console.log('authorization', data, userInfo)
9 | if ( !userInfo ) {
10 | socket.emit( CONSTS.LOGGEDUSERS_CONSTS.S2C_LOGGEDUSERS_UNAUTHORIZED, {} );
11 | return;
12 | }
13 | _.set( io, `connectedUsers.sockets.${socket.id}.info.type`, CONSTS.SOCKET_TYPES.LOGGEDUSERS );
14 |
15 | loggedusersUtils.emitLoggeduserInfo( io );
16 | }
17 |
18 | exports.disconnect = (io, socket, data) => {
19 | loggedusersUtils.removeConnected(io, socket);
20 | loggedusersUtils.emitLoggeduserInfo(io);
21 | }
22 |
23 | exports.registerAction = (io, socket, data) => {
24 | const actionData = {
25 | ...data,
26 | action: data.action || '',
27 | url: data.url || '',
28 | };
29 | _.map( actionData, ( item, index ) => {
30 | _.set( io, `connectedUsers.sockets.${socket.id}.info.${index}`, item || null );
31 | })
32 | // const action = data.action || '';
33 | // const url = data.url || '';
34 | // _.set( io, `connectedUsers.sockets.${socket.id}.info.action`, action );
35 | // _.set( io, `connectedUsers.sockets.${socket.id}.info.url`, url );
36 |
37 | loggedusersUtils.registerLog( io, socket, actionData );
38 | loggedusersUtils.emitLoggeduserInfo( io );
39 | }
--------------------------------------------------------------------------------
/client/src/containers/PageContent.js:
--------------------------------------------------------------------------------
1 | import Header from "./Header";
2 | import { Route, Routes } from "react-router-dom";
3 | import routes from "../routes";
4 | import { Suspense, lazy } from "react";
5 | import SuspenseContent from "./SuspenseContent";
6 | import { useSelector } from "react-redux";
7 | import { useEffect, useRef } from "react";
8 |
9 | const Page404 = lazy(() => import("../pages/protected/404"));
10 |
11 | function PageContent() {
12 | const mainContentRef = useRef(null);
13 | const { pageTitle } = useSelector((state) => state.header);
14 |
15 | // Scroll back to top on new page load
16 | useEffect(() => {
17 | mainContentRef.current.scroll({
18 | top: 0,
19 | behavior: "smooth",
20 | });
21 | }, [pageTitle]);
22 |
23 | return (
24 |
25 |
26 |
27 | }>
28 |
29 | {routes.map((route, key) => {
30 | return (
31 | } />
32 | );
33 | })}
34 | {/* Redirecting unknown url to 404 page */}
35 | } />
36 |
37 |
38 |
39 |
40 |
41 | );
42 | }
43 |
44 | export default PageContent;
45 |
--------------------------------------------------------------------------------
/client/src/features/dashboard/components/UserChannels.js:
--------------------------------------------------------------------------------
1 | import TitleCard from "../../../components/Cards/TitleCard";
2 |
3 | const userSourceData = [
4 | { source: "Facebook Ads", count: "26,345", conversionPercent: 10.2 },
5 | { source: "Google Ads", count: "21,341", conversionPercent: 11.7 },
6 | { source: "Instagram Ads", count: "34,379", conversionPercent: 12.4 },
7 | { source: "Affiliates", count: "12,359", conversionPercent: 20.9 },
8 | { source: "Organic", count: "10,345", conversionPercent: 10.3 },
9 | ];
10 |
11 | function UserChannels() {
12 | return (
13 |
14 | {/** Table Data */}
15 |
16 |
17 |
18 |
19 | |
20 | Source |
21 | No of Users |
22 | Conversion |
23 |
24 |
25 |
26 | {userSourceData.map((u, k) => {
27 | return (
28 |
29 | | {k + 1} |
30 | {u.source} |
31 | {u.count} |
32 | {`${u.conversionPercent}%`} |
33 |
34 | );
35 | })}
36 |
37 |
38 |
39 |
40 | );
41 | }
42 |
43 | export default UserChannels;
44 |
--------------------------------------------------------------------------------
/client/src/redux/auth.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | const checkAuth = (dispatch) => {
4 | // const dispatch = useDispatch();
5 | /* Getting token value stored in localstorage, if token is not present we will open login page
6 | for all internal dashboard routes */
7 | const TOKEN = localStorage.getItem("token");
8 | const PUBLIC_ROUTES = [
9 | "signin",
10 | "forgot-password",
11 | "signup",
12 | "documentation",
13 | ];
14 |
15 | const isPublicPage = PUBLIC_ROUTES.some((r) =>
16 | window.location.href.includes(r)
17 | );
18 |
19 | if (!TOKEN && !isPublicPage) {
20 | window.location.href = "/signin";
21 | return;
22 | } else {
23 | axios.defaults.headers.common[
24 | "Authorization"
25 | ] = `Bearer ${TOKEN}`;
26 | axios.interceptors.request.use(
27 | function (config) {
28 | // UPDATE: Add this code to show global loading indicator
29 | document.body.classList.add("loading-indicator");
30 | return config;
31 | },
32 | function (error) {
33 | return Promise.reject(error);
34 | }
35 | );
36 | axios.interceptors.response.use(
37 | function (response) {
38 | // UPDATE: Add this code to hide global loading indicator
39 | document.body.classList.remove("loading-indicator");
40 | return response;
41 | },
42 | function (error) {
43 | document.body.classList.remove("loading-indicator");
44 | return Promise.reject(error);
45 | }
46 | );
47 | return TOKEN;
48 | }
49 | };
50 |
51 | export default checkAuth;
52 |
--------------------------------------------------------------------------------
/client/src/features/charts/components/ScatterChart.js:
--------------------------------------------------------------------------------
1 | import {
2 | Chart as ChartJS,
3 | Filler,
4 | ArcElement,
5 | Tooltip,
6 | Legend,
7 | } from 'chart.js';
8 | import { Scatter } from 'react-chartjs-2';
9 | import TitleCard from '../../../components/Cards/TitleCard';
10 |
11 | ChartJS.register(ArcElement, Tooltip, Legend,
12 | Tooltip,
13 | Filler,
14 | Legend);
15 |
16 | function ScatterChart(){
17 |
18 | const options = {
19 | scales: {
20 | y: {
21 | beginAtZero: true,
22 | },
23 | },
24 | };
25 |
26 | const data = {
27 | datasets: [
28 | {
29 | label: 'Orders > 1k',
30 | data: Array.from({ length: 100 }, () => ({
31 | x: Math.random() * 11,
32 | y: Math.random() * 31,
33 | })),
34 | backgroundColor: 'rgba(255, 99, 132, 1)',
35 | },
36 | {
37 | label: 'Orders > 2K',
38 | data: Array.from({ length: 100 }, () => ({
39 | x: Math.random() * 12,
40 | y: Math.random() * 12,
41 | })),
42 | backgroundColor: 'rgba(0, 0, 255, 1)',
43 | },
44 | ],
45 | };
46 |
47 | return(
48 |
49 |
50 |
51 | )
52 | }
53 |
54 |
55 | export default ScatterChart
--------------------------------------------------------------------------------
/server/src/models/userModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 | const bcrypt = require("bcryptjs");
3 | const userSchema = new mongoose.Schema({
4 | username: {
5 | type: String,
6 | unique: [true, "Username already exist!"],
7 | required: [true, "Must be provided username"],
8 | },
9 | name: {
10 | type: String,
11 | require: true,
12 | },
13 | bio: {
14 | type: String,
15 | },
16 | password: {
17 | type: String,
18 | require: true,
19 | },
20 | role: {
21 | type: String,
22 | default: "user",
23 | },
24 | avatar: {
25 | type: String,
26 | default: "/assets/avatars/default.jpg",
27 | },
28 | followers: [
29 | {
30 | user: {
31 | type: mongoose.Schema.ObjectId,
32 | ref: "User",
33 | required: true,
34 | },
35 | },
36 | ],
37 | following: [
38 | {
39 | user: {
40 | type: mongoose.Schema.ObjectId,
41 | ref: "User",
42 | required: true,
43 | },
44 | },
45 | ],
46 | category: [String],
47 | delected: {
48 | type: Date,
49 | default: null,
50 | },
51 | complete: {
52 | type: Boolean,
53 | default: true,
54 | },
55 | createdAt: {
56 | type: Date,
57 | default: Date.now,
58 | },
59 | });
60 |
61 | userSchema.methods.hide_pwd = (password) => {
62 | let salt = bcrypt.genSaltSync(10);
63 | return bcrypt.hashSync(password, salt);
64 | };
65 |
66 | userSchema.methods.show_pwd = (reqPwd, dbPwd) => {
67 | return bcrypt.compareSync(reqPwd, dbPwd);
68 | };
69 |
70 | module.exports = mongoose.model("User", userSchema);
71 |
--------------------------------------------------------------------------------
/client/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { lazy, useEffect } from "react";
2 | import "./App.css";
3 | import {
4 | BrowserRouter as Router,
5 | Route,
6 | Routes,
7 | Navigate,
8 | } from "react-router-dom";
9 | import { themeChange } from "theme-change";
10 | import checkAuth from "./redux/auth";
11 | import initializeApp from "./redux/init";
12 | import { useDispatch } from "react-redux";
13 | import { setAuth } from "./redux/authSlice";
14 | const Layout = lazy(() => import("./containers/Layout"));
15 | const Singin = lazy(() => import("./pages/Signin"));
16 | const ForgotPassword = lazy(() => import("./pages/ForgotPassword"));
17 | const Signup = lazy(() => import("./pages/Signup"));
18 |
19 | // Initializing different libraries
20 | initializeApp();
21 |
22 | // Check for login and initialize axios
23 | const token = checkAuth();
24 |
25 | function App() {
26 | const dispatch = useDispatch();
27 | if (token) {
28 | const payload = {
29 | isAuthenicated: true,
30 | user: JSON.parse(localStorage.user),
31 | };
32 | dispatch(setAuth(payload));
33 | }
34 | useEffect(() => {
35 | themeChange(false);
36 | }, []);
37 |
38 | return (
39 | <>
40 |
41 |
42 | } />
43 | } />
44 | } />
45 | } />
46 | {/* } /> */}
47 |
48 |
49 | >
50 | );
51 | }
52 |
53 | export default App;
54 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "todo-app-backend",
3 | "version": "1.0.0",
4 | "description": "todo-app-backend development with node.js, express.js & mongoDB",
5 | "main": "app.js",
6 | "scripts": {
7 | "start": "nodemon app.js",
8 | "prod": "NODE_ENV=production node app.js",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/SamiurRahmanMukul/Complete-MERN-TODO-Application.git"
14 | },
15 | "bugs": {
16 | "url": "https://github.com/SamiurRahmanMukul/Complete-MERN-TODO-Application/issues"
17 | },
18 | "homepage": "https://github.com/SamiurRahmanMukul/Complete-MERN-TODO-Application/todo-backend#readme",
19 | "keywords": [
20 | "TODO-APP",
21 | "MERN",
22 | "MongoDB",
23 | "NodeJS",
24 | "ExpressJS",
25 | "ReactJS",
26 | "Mongoose",
27 | "Express",
28 | "MongoDB"
29 | ],
30 | "author": "Samiur Rahman Mukul",
31 | "license": "ISC",
32 | "dependencies": {
33 | "bcryptjs": "^2.4.3",
34 | "cors": "^2.8.5",
35 | "dotenv": "^16.0.1",
36 | "express": "^4.18.1",
37 | "express-fileupload": "^1.5.0",
38 | "jsonwebtoken": "^9.0.2",
39 | "mongoose": "^6.3.5",
40 | "nodemon": "^3.1.3",
41 | "passport": "^0.7.0",
42 | "passport-jwt": "^4.0.1",
43 | "serve-favicon": "^2.5.0",
44 | "socket.io": "^4.7.5"
45 | },
46 | "devDependencies": {
47 | "eslint": "^8.2.0",
48 | "eslint-config-airbnb-base": "^15.0.0",
49 | "eslint-config-prettier": "^8.5.0",
50 | "eslint-plugin-import": "^2.25.2",
51 | "eslint-plugin-prettier": "^4.0.0",
52 | "prettier": "^2.6.2"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/client/src/components/Input/SelectBox.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import capitalize from "capitalize-the-first-letter";
3 | import React, { useState, useEffect } from "react";
4 | import InformationCircleIcon from "@heroicons/react/24/outline/InformationCircleIcon";
5 |
6 | function SelectBox(props) {
7 | const {
8 | labelTitle,
9 | labelDescription,
10 | defaultValue,
11 | containerStyle,
12 | placeholder,
13 | labelStyle,
14 | options,
15 | updateType,
16 | updateFormValue,
17 | } = props;
18 |
19 | const [value, setValue] = useState(defaultValue || "");
20 |
21 | const updateValue = (newValue) => {
22 | updateFormValue({ updateType, value: newValue });
23 | setValue(newValue);
24 | };
25 |
26 | return (
27 |
28 |
38 |
39 |
51 |
52 | );
53 | }
54 |
55 | export default SelectBox;
56 |
--------------------------------------------------------------------------------
/client/src/components/Avatar.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 | import { useDispatch, useSelector } from "react-redux";
3 | import { updateAvatar } from "../redux/authSlice";
4 | import { BiSolidEditAlt } from "react-icons/bi";
5 |
6 | function ProfileAvatar() {
7 | const [avatar, setAvatar] = useState();
8 | const { user } = useSelector((state) => state.auth);
9 |
10 | const dispatch = useDispatch();
11 |
12 | const changeAvatar = (e) => {
13 | setAvatar(e.target.files[0]);
14 | };
15 |
16 | const handleSubmit = () => {
17 | dispatch(updateAvatar({ id: user._id, avatar: avatar }));
18 | };
19 |
20 | return (
21 |
22 |
23 |

28 |
33 |
34 |
35 |
43 |
44 |
45 | );
46 | }
47 |
48 | export default ProfileAvatar;
49 |
--------------------------------------------------------------------------------
/client/src/features/dashboard/components/DoughnutChart.js:
--------------------------------------------------------------------------------
1 | import {
2 | Chart as ChartJS,
3 | Filler,
4 | ArcElement,
5 | Title,
6 | Tooltip,
7 | Legend,
8 | } from 'chart.js';
9 | import { Doughnut } from 'react-chartjs-2';
10 | import TitleCard from '../../../components/Cards/TitleCard';
11 | import Subtitle from '../../../components/Typography/Subtitle';
12 |
13 | ChartJS.register(ArcElement, Tooltip, Legend,
14 | Tooltip,
15 | Filler,
16 | Legend);
17 |
18 | function DoughnutChart() {
19 |
20 | const options = {
21 | responsive: true,
22 | plugins: {
23 | legend: {
24 | position: 'top',
25 | },
26 | },
27 | };
28 |
29 | const labels = ['Electronics', 'Home Applicances', 'Beauty', 'Furniture', 'Watches', 'Apparel'];
30 |
31 | const data = {
32 | labels,
33 | datasets: [
34 | {
35 | label: '# of Orders',
36 | data: [122, 219, 30, 51, 82, 13],
37 | backgroundColor: [
38 | 'rgba(255, 99, 132, 0.8)',
39 | 'rgba(54, 162, 235, 0.8)',
40 | 'rgba(255, 206, 86, 0.8)',
41 | 'rgba(75, 192, 192, 0.8)',
42 | 'rgba(153, 102, 255, 0.8)',
43 | 'rgba(255, 159, 64, 0.8)',
44 | ],
45 | borderColor: [
46 | 'rgba(255, 99, 132, 1)',
47 | 'rgba(54, 162, 235, 1)',
48 | 'rgba(255, 206, 86, 1)',
49 | 'rgba(75, 192, 192, 1)',
50 | 'rgba(153, 102, 255, 1)',
51 | 'rgba(255, 159, 64, 1)',
52 | ],
53 | borderWidth: 1,
54 | }
55 | ],
56 | };
57 |
58 | return (
59 |
60 |
61 |
62 | )
63 | }
64 |
65 |
66 | export default DoughnutChart
--------------------------------------------------------------------------------
/client/src/features/charts/components/StackBarChart.js:
--------------------------------------------------------------------------------
1 | import {
2 | Chart as ChartJS,
3 | CategoryScale,
4 | LinearScale,
5 | BarElement,
6 | Title,
7 | Tooltip,
8 | Legend,
9 | } from 'chart.js';
10 | import { Bar } from 'react-chartjs-2';
11 | import TitleCard from '../../../components/Cards/TitleCard';
12 |
13 | ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);
14 |
15 | function StackBarChart(){
16 |
17 | const options = {
18 | responsive: true,
19 | scales: {
20 | x: {
21 | stacked: true,
22 | },
23 | y: {
24 | stacked: true,
25 | },
26 | },
27 | };
28 |
29 | const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
30 |
31 | const data = {
32 | labels,
33 | datasets: [
34 | {
35 | label: 'Store 1',
36 | data: labels.map(() => { return Math.random() * 1000 + 500 }),
37 | backgroundColor: 'rgba(255, 99, 132, 1)',
38 | },
39 | {
40 | label: 'Store 2',
41 | data: labels.map(() => { return Math.random() * 1000 + 500 }),
42 | backgroundColor: 'rgba(53, 162, 235, 1)',
43 | },
44 | {
45 | label: 'Store 3',
46 | data: labels.map(() => { return Math.random() * 1000 + 500 }),
47 | backgroundColor: 'rgba(235, 162, 235, 1)',
48 | },
49 | ],
50 | };
51 |
52 | return(
53 |
54 |
55 |
56 |
57 | )
58 | }
59 |
60 |
61 | export default StackBarChart
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "finalgoal",
3 | "version": "1.0.0",
4 | "description": "Admin Dashboard template built with create-react-app, tailwind css and daisy UI. Template uses rich tailwind css utility classes and have components of daisy UI, also have redux toolkit implemented for store management.",
5 | "scripts": {
6 | "start": "concurrently \"npm run dev:frontend\"",
7 | "start:frontend": "npm -w ./client run start",
8 | "start:backend": "npm -w ./server run start",
9 | "build:frontend": "npm -w ./client run build",
10 | "dev": "concurrently \"npm run dev:frontend\" \"npm run dev:backend\"",
11 | "dev:frontend": "npm -w ./client run start",
12 | "dev:backend": "npm -w ./server run start",
13 | "test": "npx playwright test",
14 | "test:ui": "npx playwright test --ui",
15 | "postinstall": "npx patch-package"
16 | },
17 | "workspaces": [
18 | "./*"
19 | ],
20 | "dependencies": {
21 | "concurrently": "^8.2.2",
22 | "patch-package": "^8.0.0"
23 | },
24 | "devDependencies": {
25 | "@playwright/test": "^1.42.1",
26 | "@types/node": "^20.11.25",
27 | "dotenv": "^16.4.5",
28 | "prettier": "^3.2.5",
29 | "tsup": "^8.0.1",
30 | "tsx": "^4.7.0",
31 | "typescript": "^5.3.3"
32 | },
33 | "repository": {
34 | "type": "git",
35 | "url": "git+https://github.com/finalgoal231/finalgoal"
36 | },
37 | "keywords": [
38 | "reactjs",
39 | "tailwind-css",
40 | "starter-kit",
41 | "saas-starter-kit",
42 | "reduxt-toolkit-dashboard-template",
43 | "daisyui-template",
44 | "dashboard-template",
45 | "react-router",
46 | "react-charts"
47 | ],
48 | "author": "shinevue",
49 | "license": "ISC",
50 | "bugs": {
51 | "url": "https://github.com/finalgoal231/finalgoal/issues"
52 | },
53 | "homepage": "",
54 | "eslintConfig": {
55 | "extends": [
56 | "react-app",
57 | "react-app/jest"
58 | ]
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/client/src/features/charts/index.js:
--------------------------------------------------------------------------------
1 | import LineChart from './components/LineChart'
2 | import BarChart from './components/BarChart'
3 | import DoughnutChart from './components/DoughnutChart'
4 | import PieChart from './components/PieChart'
5 | import ScatterChart from './components/ScatterChart'
6 | import StackBarChart from './components/StackBarChart'
7 | import Datepicker from "react-tailwindcss-datepicker";
8 | import { useState } from 'react'
9 |
10 |
11 |
12 |
13 | function Charts(){
14 |
15 | const [dateValue, setDateValue] = useState({
16 | startDate: new Date(),
17 | endDate: new Date()
18 | });
19 |
20 | const handleDatePickerValueChange = (newValue) => {
21 | console.log("newValue:", newValue);
22 | setDateValue(newValue);
23 | }
24 |
25 | return(
26 | <>
27 |
38 | {/** ---------------------- Different charts ------------------------- */}
39 |
40 |
41 |
42 |
43 |
44 |
45 |
49 |
50 |
51 |
52 |
53 |
54 | >
55 | )
56 | }
57 |
58 | export default Charts
--------------------------------------------------------------------------------
/client/src/features/charts/components/DoughnutChart.js:
--------------------------------------------------------------------------------
1 | import {
2 | Chart as ChartJS,
3 | Filler,
4 | ArcElement,
5 | Title,
6 | Tooltip,
7 | Legend,
8 | } from 'chart.js';
9 | import { Doughnut } from 'react-chartjs-2';
10 | import TitleCard from '../../../components/Cards/TitleCard';
11 | import Subtitle from '../../../components/Typography/Subtitle';
12 |
13 | ChartJS.register(ArcElement, Tooltip, Legend,
14 | Tooltip,
15 | Filler,
16 | Legend);
17 |
18 | function DoughnutChart(){
19 |
20 | const options = {
21 | responsive: true,
22 | plugins: {
23 | legend: {
24 | position: 'top',
25 | },
26 | },
27 | };
28 |
29 | const labels = ['Electronics', 'Home Applicances', 'Beauty', 'Furniture', 'Watches', 'Apparel'];
30 |
31 | const data = {
32 | labels,
33 | datasets: [
34 | {
35 | label: '# of Orders',
36 | data: [122, 219, 30, 51, 82, 13],
37 | backgroundColor: [
38 | 'rgba(255, 99, 132, 0.8)',
39 | 'rgba(54, 162, 235, 0.8)',
40 | 'rgba(255, 206, 86, 0.8)',
41 | 'rgba(75, 192, 192, 0.8)',
42 | 'rgba(153, 102, 255, 0.8)',
43 | 'rgba(255, 159, 64, 0.8)',
44 | ],
45 | borderColor: [
46 | 'rgba(255, 99, 132, 1)',
47 | 'rgba(54, 162, 235, 1)',
48 | 'rgba(255, 206, 86, 1)',
49 | 'rgba(75, 192, 192, 1)',
50 | 'rgba(153, 102, 255, 1)',
51 | 'rgba(255, 159, 64, 1)',
52 | ],
53 | borderWidth: 1,
54 | }
55 | ],
56 | };
57 |
58 | return(
59 |
60 |
61 |
62 | )
63 | }
64 |
65 |
66 | export default DoughnutChart
--------------------------------------------------------------------------------
/server/socket/events/authorizationEvents.js:
--------------------------------------------------------------------------------
1 | const jwt = require('jsonwebtoken');
2 | const _ = require('lodash');
3 |
4 | const utils = require('../utils');
5 | const authUtils = require('../utils/authorizationUtils');
6 | const chatUtils = require('../utils/chatUtils');
7 | const loggedusersUtils = require('../utils/loggedusersUtils');
8 | const CONSTS = require('../constants');
9 | const config = require('../../_main/config');
10 |
11 | exports.authorization = (io, socket, data) => {
12 | const token = utils.parseToken(data.token);
13 | if ( !token ) {
14 | socket.disconnect(CONSTS.AUTHORIZATION_CONSTS.S2C_AUTH_UNAUTHORIZED);
15 | return;
16 | }
17 | jwt.verify(token, config.secret, null, (err, decoded) => {
18 | if (err) {
19 | socket.disconnect(CONSTS.AUTHORIZATION_CONSTS.S2C_AUTH_UNAUTHORIZED);
20 | return;
21 | }
22 | socket.emit(CONSTS.AUTHORIZATION_CONSTS.S2C_AUTH_AUTHORIZED);
23 | decoded._id = decoded.id
24 | authUtils.registerConnected(io, socket, decoded);
25 | authUtils.emitUserlistInfos(io);
26 | loggedusersUtils.emitLoggeduserInfo(io);
27 | })
28 | }
29 |
30 | exports.registerPageUrl = (io, socket, data) => {
31 | if ( io.connectedUsers && io.connectedUsers.sockets && io.connectedUsers.sockets[socket.id] ) {
32 | // console.log('here', data, 'socketId=', socket.id)
33 | const origin = ( ( _.get( socket, 'handshake.headers.origin' ) || '' ).split( ':' ) || [] )[0];
34 | io.connectedUsers.sockets[socket.id].lastConnected = ( new Date() ).getTime();
35 | io.connectedUsers.sockets[socket.id].pageUrl = data.pageUrl;
36 | io.connectedUsers.sockets[socket.id].origin = origin || '';
37 | loggedusersUtils.registerLog( io, socket, {url: data.pageUrl} );
38 | loggedusersUtils.emitLoggeduserInfo( io );
39 | // console.log(io.connectedUsers.sockets)
40 | }
41 | }
42 |
43 | exports.disconnect = (io, socket) => {
44 | // const userId = chatUtils.getUserIdFromSocket(io, socket);
45 | authUtils.removeConnected(io, socket);
46 | authUtils.emitUserlistInfos(io);
47 | loggedusersUtils.emitLoggeduserInfo(io);
48 | }
--------------------------------------------------------------------------------
/client/src/containers/ModalLayout.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { MODAL_BODY_TYPES } from "../utils/globalConstantUtil";
3 | import { useSelector, useDispatch } from "react-redux";
4 | import { closeModal } from "../features/common/modalSlice";
5 | import AddLeadModalBody from "../features/leads/components/AddLeadModalBody";
6 | import ConfirmationModalBody from "../features/common/components/ConfirmationModalBody";
7 |
8 | function ModalLayout() {
9 | const { isOpen, bodyType, size, extraObject, title } = useSelector((state) => state.modal);
10 | const dispatch = useDispatch();
11 |
12 | const close = (e) => {
13 | dispatch(closeModal(e));
14 | };
15 |
16 | return (
17 | <>
18 | {/* The button to open modal */}
19 |
20 | {/* Put this part before
31 |
32 |
33 |
43 | tag */}
21 |
22 |
23 |
26 |
{title}
27 |
28 | {/* Loading modal body according to different modal type */}
29 | {
30 | {
31 | [MODAL_BODY_TYPES.LEAD_ADD_NEW]: (
32 |
33 | ),
34 | [MODAL_BODY_TYPES.CONFIRMATION]: (
35 |
36 | ),
37 | [MODAL_BODY_TYPES.DEFAULT]:
,
38 | }[bodyType]
39 | }
40 |
41 |
42 | >
43 | );
44 | }
45 |
46 | export default ModalLayout;
47 |
--------------------------------------------------------------------------------
/client/src/features/charts/components/PieChart.js:
--------------------------------------------------------------------------------
1 | import {
2 | Chart as ChartJS,
3 | Filler,
4 | ArcElement,
5 | Title,
6 | Tooltip,
7 | Legend,
8 | } from 'chart.js';
9 | import { Pie } from 'react-chartjs-2';
10 | import TitleCard from '../../../components/Cards/TitleCard';
11 | import Subtitle from '../../../components/Typography/Subtitle';
12 |
13 | ChartJS.register(ArcElement, Tooltip, Legend,
14 | Tooltip,
15 | Filler,
16 | Legend);
17 |
18 | function PieChart(){
19 |
20 | const options = {
21 | responsive: true,
22 | plugins: {
23 | legend: {
24 | position: 'top',
25 | },
26 | },
27 | };
28 |
29 | const labels = ['India', 'Middle East', 'Europe', 'US', 'Latin America', 'Asia(non-india)'];
30 |
31 | const data = {
32 | labels,
33 | datasets: [
34 | {
35 | label: '# of Orders',
36 | data: [122, 219, 30, 51, 82, 13],
37 | backgroundColor: [
38 | 'rgba(255, 99, 255, 0.8)',
39 | 'rgba(54, 162, 235, 0.8)',
40 | 'rgba(255, 206, 255, 0.8)',
41 | 'rgba(75, 192, 255, 0.8)',
42 | 'rgba(153, 102, 255, 0.8)',
43 | 'rgba(255, 159, 255, 0.8)',
44 | ],
45 | borderColor: [
46 | 'rgba(255, 99, 255, 1)',
47 | 'rgba(54, 162, 235, 1)',
48 | 'rgba(255, 206, 255, 1)',
49 | 'rgba(75, 192, 255, 1)',
50 | 'rgba(153, 102, 255, 1)',
51 | 'rgba(255, 159, 255, 1)',
52 | ],
53 | borderWidth: 1,
54 | }
55 | ],
56 | };
57 |
58 | return(
59 |
60 |
61 |
62 | )
63 | }
64 |
65 |
66 | export default PieChart
--------------------------------------------------------------------------------
/server/src/controllers/adminControllers/categoryController.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 |
3 | const Category = require("../../models/adminModel/categoryModel");
4 |
5 | // make a controller for create a category
6 | exports.createCategory = (req, res) => {
7 | const newCategory = new Category(req.body);
8 | newCategory
9 | .save()
10 | .then(() => {
11 | res.status(201).json({ msg: "create category successfully." });
12 | })
13 | .catch(() => {
14 | res.status(500).json({ msg: "Can't do action article" });
15 | });
16 | };
17 |
18 | // make a controller for update a category
19 | exports.updateCategory = (req, res) => {
20 | let id = req.params.id;
21 | let data = req.body;
22 | Category.findByIdAndUpdate(id, data)
23 | .then(() => {
24 | res.status(201).json({ msg: "Updated successfully." });
25 | })
26 | .catch(() => {
27 | res.status(500).json({ msg: "Can't do action article" });
28 | });
29 | };
30 |
31 | // make a controller for deletea category
32 | exports.deleteCategory = (req, res) => {
33 | let id = req.params.id;
34 | Category.findById(id)
35 | .then((category) => {
36 | category.delected = new Date();
37 | category
38 | .save()
39 | .then(() => {
40 | res.status(201).json({ msg: "Category deleted successfully" });
41 | })
42 | .catch(() => {
43 | res.status(400).json({ msg: "Invalide category." });
44 | });
45 | })
46 | .catch((err) => {
47 | res.status(400).json({ msg: "Invalide category." });
48 | });
49 | };
50 |
51 | // make a controller for get all category
52 | exports.getAllCategory = (req, res) => {
53 | const { sortIndex, searchVal } = req.query;
54 | const query = {
55 | delected: null,
56 | // title: { $regex: new RegExp(searchVal, "i") },
57 | };
58 | console.log(query);
59 | Category.find(query)
60 | .sort({ [sortIndex]: -1 })
61 | .then((result) => {
62 | res.status(201).json({ result: result });
63 | })
64 | .catch((err) => {
65 | res.status(400).json({ err: err });
66 | });
67 | };
68 |
--------------------------------------------------------------------------------
/client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 |
Daisy UI Admin Dashboard Template - DashWind
28 |
29 |
30 |
44 |