├── README.md
├── admin
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── src
│ ├── App.js
│ ├── assets
│ │ ├── image.svg
│ │ ├── profile.png
│ │ └── writer.jpg
│ ├── components
│ │ ├── Comments.jsx
│ │ ├── ConfirmDialog.jsx
│ │ ├── Graph.jsx
│ │ ├── Loading.jsx
│ │ ├── LoginForm.jsx
│ │ ├── Logo.jsx
│ │ ├── Navbar.jsx
│ │ ├── PasswordStrength.jsx
│ │ ├── Sidebar.jsx
│ │ ├── SignUpForm.jsx
│ │ ├── Stats.jsx
│ │ └── Table.jsx
│ ├── hooks
│ │ ├── auth-hook.js
│ │ ├── followers-hook.js
│ │ └── post-hook.js
│ ├── index.css
│ ├── index.js
│ ├── pages
│ │ ├── Analytics.jsx
│ │ ├── Content.jsx
│ │ ├── Dashboard.jsx
│ │ ├── Followers.jsx
│ │ ├── OTPVerification.jsx
│ │ ├── StartPage.jsx
│ │ ├── StartPage.sx
│ │ └── WritePost.jsx
│ ├── store
│ │ ├── commentStore.js
│ │ └── index.js
│ └── utils
│ │ ├── dummyData.js
│ │ ├── firebase.js
│ │ └── index.js
└── tailwind.config.js
├── client
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── src
│ ├── App.js
│ ├── assets
│ │ └── profile.png
│ ├── components
│ │ ├── Banner.jsx
│ │ ├── Button.jsx
│ │ ├── Card.jsx
│ │ ├── Divider.jsx
│ │ ├── Footer.jsx
│ │ ├── Inputbox.jsx
│ │ ├── Loading.jsx
│ │ ├── Logo.jsx
│ │ ├── Navbar.jsx
│ │ ├── Pagination.jsx
│ │ ├── PopularPosts.jsx
│ │ ├── PopularWriters.jsx
│ │ ├── PostComments.jsx
│ │ ├── Switch.jsx
│ │ └── index.js
│ ├── index.css
│ ├── index.js
│ ├── pages
│ │ ├── BlogDetails.jsx
│ │ ├── CategoriesPage.jsx
│ │ ├── Home.jsx
│ │ ├── LoginPage.jsx
│ │ ├── SignupPage.1.jsx
│ │ ├── SignupPage.jsx
│ │ ├── WriterPage.jsx
│ │ └── index.js
│ ├── store
│ │ └── index.js
│ └── utils
│ │ ├── dummyData.js
│ │ └── index.js
└── tailwind.config.js
└── server
├── controllers
├── authController.js
├── postController.js
└── userController.js
├── dbConfig
└── index.js
├── index.js
├── middleware
├── authMiddleware.js
└── errorMiddleware.js
├── models
├── commentModel.js
├── emailVerification.js
├── followersModel.js
├── postModel.js
├── userModel.js
└── viewsModel.js
├── package.json
├── routes
├── authRoute.js
├── index.js
├── postRoute.js
└── userRoute.js
└── utils
├── index.js
└── sendEmail.js
/README.md:
--------------------------------------------------------------------------------
1 | # Fullstack Blog Application (MERN Stack)
2 |
3 | Welcome to the Fullstack Blog Application documentation! This application allows users to explore a variety of blog posts with a user-friendly interface. Below is a detailed guide on how to set up and run the application.
4 |
5 |
6 |
7 | ## Overview
8 |
9 | This full-stack blog application is built using the MERN stack (MongoDB, Express, React, Node.js). The state management is handled using Zustand instead of Redux. Users can enjoy features like post categorization, pagination, commenting, and a dual Dark & Light theme UI. Admins have access to additional functionalities in the Admin Dashboard, including analytics on post content and views.
10 |
11 |
12 |
13 | ## System Requirements:
14 |
15 | - Node.js version 18 or above.
16 | - npm version 10.2.3.
17 | - MongoDB Atlas
18 | - Text Editor or IDE
19 | - Git
20 | - Postman (Optional)
21 |
22 |
23 |
24 | ## Technologies Used:
25 |
26 | 1. ReactJs
27 | 2. NodeJs (Node version 18 or above)
28 | 3. ExpressJs
29 | 4. MongoDB (Database)
30 | 5. Tailwind CSS (for Styling)
31 | 6. Zustand (for State Management)
32 |
33 |
34 |
35 | ## Features Include (Client side):
36 |
37 | 1. User Account Creation (Optional)
38 | 2. Google Sign In (Optional) - Client side only
39 | 3. Post Categories
40 | 4. Pagination with Page Numbers
41 | 5. Commenting on Posts (Only Available to Signed-in Users)
42 | 6. Dark and Light Theme Settings
43 | 7. Blog Details Page with Dynamic URL (Slug)
44 | 8. Fully Mobile-Responsive Design
45 |
46 | ## Features Include (Admin Dashboard):
47 |
48 | 1. Account Creation
49 | 2. Email Verification with OTP
50 | 3. Create Posts, Delete Posts, Enable or Disable Posts
51 | 4. Dashboard Analytics
52 | 5. Content and Views Analytics (7 days, 28 days, 900 days & 365 days)
53 | 6. Ability to delete user comments on a blog post
54 | 7. Dark and Light Theme Settings
55 | 8. Fully Mobile-Responsive Design
56 |
57 | ## Modern User Interface (UI)
58 |
59 | Classic UI with DARK & LIGHT theme settings
60 |
61 |
62 |
63 | ## Screenshots:-
64 |
65 | ### Dark Theme >>
66 | 
67 |
68 |
69 | 
70 |
71 | 
72 |
73 | 
74 |
75 | 
76 |
77 | ### Light Theme >>
78 |
79 | 
80 |
81 |
82 | ### Admin Dashboard >>
83 |
84 | 
85 |
86 |
87 |
88 |
89 | # Server Setup
90 |
91 | ## Environment variables
92 | First, create the environment variables file `.env` in the server folder. The `.env` file contains the following environment variables:
93 |
94 | - MONGODB_URL = `your MongoDB URL`
95 | - JWT_SECRET_KEY = `any secret key - must be secured`
96 | - PORT = `8800` or any port number
97 | - AUTH_EMAIL = `your email address to send the OTP`
98 | - AUTH_PASSWORD = `password to your email account` (used Hotmail for email verification)
99 |
100 |
101 |
102 | ## Set Up MongoDB:
103 |
104 | 1. Setting up MongoDB involves a few steps:
105 | - Visit MongoDB Atlas Website
106 | - Go to the MongoDB Atlas website: [https://www.mongodb.com/cloud/atlas](https://www.mongodb.com/cloud/atlas).
107 |
108 | - Create an Account
109 | - Log in to your MongoDB Atlas account.
110 | - Create a New Cluster
111 | - Choose a Cloud Provider and Region
112 | - Configure Cluster Settings
113 | - Create Cluster
114 | - Wait for Cluster to Deploy
115 | - Create Database User
116 | - Set Up IP Whitelist
117 | - Connect to Cluster
118 | - Configure Your Application
119 | - Test the Connection
120 |
121 | 2. Create a new database and configure the `.env` file with the MongoDB connection URL.
122 |
123 | ## Steps to run server
124 |
125 | 1. Open the project in any editor of choice.
126 | 2. Navigate into the server directory `cd server`.
127 | 3. Run `npm i` to install the packages.
128 | 4. Run `npm start` to start the server.
129 |
130 | If configured correctly, you should see a message indicating that the server is running successfully and `Database Connected`.
131 |
132 |
133 |
134 | # Client Side Setup
135 |
136 | ## Environment variables
137 | First, create the environment variables file `.env` in the client folder. The `.env` file contains the following environment variables:
138 |
139 | - REACT_APP_GOOGLE_CLIENT_ID = `Google client ID for Google Sign In`
140 | - REACT_APP_FIREBASE_API_KEY = `Firebase key`
141 |
142 | ## Steps to run client
143 |
144 | 1. Navigate into the client directory `cd client`.
145 | 2. Run `npm i` to install the packages.
146 | 3. Run `npm start` to run the app on `http://localhost:3000`.
147 | 4. Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
148 |
149 |
150 |
151 | # Admin Dashboard Setup
152 |
153 | ## Environment variables
154 | First, create the environment variables file `.env` in the admin folder. The `.env` file contains the following environment variables:
155 |
156 | - REACT_APP_FIREBASE_API_KEY = `Firebase key`
157 |
158 | ## Steps to run admin dashboard
159 |
160 | 1. Navigate into the admin directory `cd admin`.
161 | 2. Run `npm i` to install the packages.
162 | 3. Run `npm start` to run the app on `http://localhost:3000` or any other available port.
163 |
164 |
165 |
166 | # Security Note:
167 |
168 | ## Environment Variables:
169 |
170 | - Safeguard your environment variables by storing them securely and not exposing them unintentionally.
171 |
172 | - Ensure that only authorized personnel have access to the environment variable configurations.
173 |
174 |
175 |
176 |
177 | ## For Support, Contact:
178 |
179 | - Email: codewavewithasante@gmail.com
180 | - Telegram Chat: [https://t.me/Codewave_with_asante](https://t.me/Codewave_with_asante)
--------------------------------------------------------------------------------
/admin/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
13 |
14 | The page will reload when you make changes.\
15 | You may also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!**
35 |
36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
39 |
40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
48 | ### Code Splitting
49 |
50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
51 |
52 | ### Analyzing the Bundle Size
53 |
54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
55 |
56 | ### Making a Progressive Web App
57 |
58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
59 |
60 | ### Advanced Configuration
61 |
62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
63 |
64 | ### Deployment
65 |
66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
67 |
68 | ### `npm run build` fails to minify
69 |
70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
71 |
--------------------------------------------------------------------------------
/admin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "admin",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@mantine/core": "^7.1.3",
7 | "@mantine/form": "^7.1.3",
8 | "@mantine/hooks": "^7.1.3",
9 | "@mantine/modals": "^7.1.3",
10 | "@mantine/tiptap": "^7.1.3",
11 | "@tabler/icons-react": "^2.39.0",
12 | "@tanstack/react-query": "^5.0.5",
13 | "@testing-library/jest-dom": "^5.17.0",
14 | "@testing-library/react": "^13.4.0",
15 | "@testing-library/user-event": "^13.5.0",
16 | "@tiptap/extension-code-block-lowlight": "^2.1.12",
17 | "@tiptap/extension-color": "^2.1.12",
18 | "@tiptap/extension-highlight": "^2.1.12",
19 | "@tiptap/extension-link": "^2.1.12",
20 | "@tiptap/extension-placeholder": "^2.1.12",
21 | "@tiptap/extension-subscript": "^2.1.12",
22 | "@tiptap/extension-superscript": "^2.1.12",
23 | "@tiptap/extension-text-align": "^2.1.12",
24 | "@tiptap/extension-text-style": "^2.1.12",
25 | "@tiptap/extension-underline": "^2.1.12",
26 | "@tiptap/pm": "^2.1.12",
27 | "@tiptap/react": "^2.1.12",
28 | "@tiptap/starter-kit": "^2.1.12",
29 | "axios": "^1.5.1",
30 | "clsx": "^2.0.0",
31 | "firebase": "^10.7.1",
32 | "lowlight": "^3.1.0",
33 | "moment": "^2.29.4",
34 | "react": "^18.2.0",
35 | "react-dom": "^18.2.0",
36 | "react-icons": "^4.11.0",
37 | "react-router-dom": "^6.17.0",
38 | "react-scripts": "5.0.1",
39 | "recharts": "^2.9.0",
40 | "sonner": "^1.0.3",
41 | "web-vitals": "^2.1.4",
42 | "zustand": "^4.4.4"
43 | },
44 | "scripts": {
45 | "start": "react-scripts start",
46 | "build": "react-scripts build",
47 | "test": "react-scripts test",
48 | "eject": "react-scripts eject"
49 | },
50 | "eslintConfig": {
51 | "extends": [
52 | "react-app",
53 | "react-app/jest"
54 | ]
55 | },
56 | "browserslist": {
57 | "production": [
58 | ">0.2%",
59 | "not dead",
60 | "not op_mini all"
61 | ],
62 | "development": [
63 | "last 1 chrome version",
64 | "last 1 firefox version",
65 | "last 1 safari version"
66 | ]
67 | },
68 | "devDependencies": {
69 | "postcss": "^8.4.31",
70 | "postcss-preset-mantine": "^1.9.0",
71 | "postcss-simple-vars": "^7.0.1",
72 | "tailwindcss": "^3.3.5"
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/admin/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeWaveWithAsante/fullstack_blog_app/657972bddcee6bb7d4eedbddabf9cd1988a3ccee/admin/public/favicon.ico
--------------------------------------------------------------------------------
/admin/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/admin/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeWaveWithAsante/fullstack_blog_app/657972bddcee6bb7d4eedbddabf9cd1988a3ccee/admin/public/logo192.png
--------------------------------------------------------------------------------
/admin/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeWaveWithAsante/fullstack_blog_app/657972bddcee6bb7d4eedbddabf9cd1988a3ccee/admin/public/logo512.png
--------------------------------------------------------------------------------
/admin/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/admin/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/admin/src/App.js:
--------------------------------------------------------------------------------
1 | import { Navigate, Outlet, Route, Routes, useLocation } from "react-router-dom";
2 | import Navbar from "./components/Navbar";
3 | import Sidebar from "./components/Sidebar";
4 | import OTPVerification from "./pages/OTPVerification";
5 | import StartPage from "./pages/StartPage";
6 | import useStore from "./store/index";
7 | import Dashboard from "./pages/Dashboard";
8 | import Analytics from "./pages/Analytics";
9 | import Followers from "./pages/Followers";
10 | import Contents from "./pages/Content";
11 | import WritePost from "./pages/WritePost";
12 |
13 | function Layout() {
14 | const { user } = useStore((state) => state);
15 |
16 | const location = useLocation();
17 |
18 | return user?.token ? (
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | ) : (
32 |
33 | );
34 | }
35 |
36 | function App() {
37 | return (
38 |
39 |
40 | }>
41 | } />
42 | } />
43 | } />
44 | } />
45 | } />
46 | } />
47 |
48 |
49 | } />
50 | } />
51 |
52 |
53 | );
54 | }
55 |
56 | export default App;
57 |
--------------------------------------------------------------------------------
/admin/src/assets/profile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeWaveWithAsante/fullstack_blog_app/657972bddcee6bb7d4eedbddabf9cd1988a3ccee/admin/src/assets/profile.png
--------------------------------------------------------------------------------
/admin/src/assets/writer.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeWaveWithAsante/fullstack_blog_app/657972bddcee6bb7d4eedbddabf9cd1988a3ccee/admin/src/assets/writer.jpg
--------------------------------------------------------------------------------
/admin/src/components/Comments.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import useCommentStore from "../store/commentStore";
3 | import useStore from "../store";
4 | import { Modal } from "@mantine/core";
5 | import { useComments, useDeleteComment } from "../hooks/post-hook";
6 | import NoProfile from "../assets/profile.png";
7 |
8 | const Comments = () => {
9 | const { openComment, commentId, setOpen } = useCommentStore();
10 | const { user } = useStore();
11 |
12 | const { data, mutate } = useComments();
13 |
14 | const useDelete = useDeleteComment(user?.token);
15 |
16 | const handleClose = () => {
17 | setOpen(false);
18 | };
19 |
20 | const handleDelete = (id) => {
21 | useDelete.mutate({ id, postId: commentId });
22 | };
23 |
24 | useEffect(() => {
25 | mutate(commentId);
26 | }, [commentId]);
27 |
28 | return (
29 | <>
30 |
37 |
38 |
39 | {data?.data?.map(({ _id, user, desc, post, createdAt }) => {
40 |
41 |

46 |
47 |
48 |
49 |
50 |
51 | {user.name}
52 |
53 |
54 | {new Date(createdAt).toDateString()}
55 |
56 |
57 |
58 |
handleDelete(_id)}
61 | >
62 | Delete
63 |
64 |
65 |
66 |
67 | {desc}
68 |
69 |
70 |
;
71 | })}
72 |
73 |
74 |
75 | >
76 | );
77 | };
78 |
79 | export default Comments;
80 |
--------------------------------------------------------------------------------
/admin/src/components/ConfirmDialog.jsx:
--------------------------------------------------------------------------------
1 | import { Button, Modal } from "@mantine/core";
2 |
3 | const ConfirmDialog = ({ message, opened, close, handleClick }) => {
4 | return (
5 |
6 | {message}
7 |
8 |
9 |
15 |
21 |
22 |
23 | );
24 | };
25 |
26 | export default ConfirmDialog;
27 |
--------------------------------------------------------------------------------
/admin/src/components/Graph.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | Area,
3 | AreaChart,
4 | ResponsiveContainer,
5 | Tooltip,
6 | XAxis,
7 | YAxis,
8 | } from "recharts";
9 |
10 | const Graph = ({ dt }) => {
11 | return (
12 |
13 | {dt?.length > 0 ? (
14 |
15 |
16 |
17 |
18 |
24 |
25 | ) : (
26 |
31 | )}
32 |
33 | );
34 | };
35 |
36 | export default Graph;
37 |
--------------------------------------------------------------------------------
/admin/src/components/Loading.jsx:
--------------------------------------------------------------------------------
1 | import { LoadingOverlay } from "@mantine/core";
2 |
3 | export default function Loading({ visible, toggle }) {
4 | return (
5 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/admin/src/components/LoginForm.jsx:
--------------------------------------------------------------------------------
1 | import { Button, Group, TextInput, useMantineColorScheme } from "@mantine/core";
2 | import { useForm } from "@mantine/form";
3 | import { useInputState } from "@mantine/hooks";
4 | import clsx from "clsx";
5 | import React, { useState } from "react";
6 | import { useNavigate } from "react-router-dom";
7 | import useStore from "../store/index";
8 | import { PasswordStrength } from "./PasswordStrength";
9 | import { useSignin } from "../hooks/auth-hook";
10 |
11 | const LoginForm = ({ toast, isSignin, setIsSignin, toggle, setFormClose }) => {
12 | const { colorScheme } = useMantineColorScheme();
13 | const theme = colorScheme === "dark";
14 |
15 | const { signIn } = useStore();
16 | const { data, isPemding, isSuccess, mutate } = useSignin(toast, toggle);
17 |
18 | const [strength, setStrength] = useState(0);
19 | const [passValue, setPassValue] = useInputState("");
20 | const navigate = useNavigate();
21 |
22 | const form = useForm({
23 | initialValues: {
24 | email: "",
25 | },
26 | validate: {
27 | email: (value) => (/^\S+@\S+$/.test(value) ? null : "Invalid email"),
28 | },
29 | });
30 |
31 | const handleSubmit = async (values) => {
32 | setFormClose(true);
33 |
34 | mutate({
35 | ...values,
36 | password: passValue,
37 | });
38 |
39 | if (isSuccess) {
40 | setFormClose(false);
41 | setTimeout(() => {
42 | signIn(data);
43 |
44 | navigate("/dashboard");
45 | }, 2000);
46 | }
47 | };
48 |
49 | return (
50 |
92 | );
93 | };
94 |
95 | export default LoginForm;
96 |
--------------------------------------------------------------------------------
/admin/src/components/Logo.jsx:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom";
2 |
3 | const Logo = ({ type }) => {
4 | return (
5 |
6 |
10 | Blog
11 |
14 | Wave
15 |
16 |
17 |
18 | );
19 | };
20 |
21 | export default Logo;
22 |
--------------------------------------------------------------------------------
/admin/src/components/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | Button,
3 | Drawer,
4 | Menu,
5 | rem,
6 | useMantineColorScheme,
7 | } from "@mantine/core";
8 | import { useDisclosure } from "@mantine/hooks";
9 | import { IconTrash } from "@tabler/icons-react";
10 | import clsx from "clsx";
11 | import React from "react";
12 | import { AiOutlineLogout } from "react-icons/ai";
13 | import { BiMenu } from "react-icons/bi";
14 | import {
15 | FaFacebook,
16 | FaInstagram,
17 | FaTwitterSquare,
18 | FaUser,
19 | FaYoutube,
20 | } from "react-icons/fa";
21 | import { MdArrowForward } from "react-icons/md";
22 | import { Link, useLocation } from "react-router-dom";
23 | import useStore from "../store";
24 | import Logo from "./Logo";
25 | import Sidebar from "./Sidebar";
26 |
27 | const MobileDrawer = ({ theme }) => {
28 | const { user } = useStore();
29 | const [opened, { open, close }] = useDisclosure(false);
30 |
31 | return (
32 | <>
33 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
50 |
51 |
52 |
58 | >
59 | );
60 | };
61 |
62 | function UserMenu({ user, theme }) {
63 | const { signOut } = useStore();
64 |
65 | const handleSignOut = () => {
66 | localStorage.removeItem("user");
67 | signOut();
68 | };
69 |
70 | return (
71 |
120 | );
121 | }
122 |
123 | const Navbar = () => {
124 | const { colorScheme } = useMantineColorScheme();
125 |
126 | const { user, signInModal, setSignInModal } = useStore();
127 | const location = useLocation();
128 | const theme = colorScheme === "dark";
129 |
130 | const handleLogin = () => {
131 | location.pathname === "/auth" && setSignInModal(!signInModal);
132 | };
133 |
134 | return (
135 |
136 | {user && (
137 |
138 |
139 |
140 | )}
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | {user?.token ? (
162 |
163 | ) : (
164 |
172 | Login
173 |
174 |
175 | )}
176 |
177 |
178 |
179 | );
180 | };
181 |
182 | export default Navbar;
183 |
--------------------------------------------------------------------------------
/admin/src/components/PasswordStrength.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | Box,
3 | Progress,
4 | PasswordInput,
5 | Group,
6 | Text,
7 | Center,
8 | } from "@mantine/core";
9 | import { IconCheck, IconX } from "@tabler/icons-react";
10 |
11 | function PasswordRequirement({ meets, label }) {
12 | return (
13 |
14 |
15 | {meets ? (
16 |
17 | ) : (
18 |
19 | )}
20 | {label}
21 |
22 |
23 | );
24 | }
25 |
26 | const requirements = [
27 | { re: /[0-9]/, label: "Includes number" },
28 | { re: /[a-z]/, label: "Includes lowercase letter" },
29 | { re: /[A-Z]/, label: "Includes uppercase letter" },
30 | { re: /[$&+,:;=?@#|'<>.^*()%!-]/, label: "Includes special symbol" },
31 | ];
32 |
33 | function getStrength(password) {
34 | let multiplier = password.length > 5 ? 0 : 1;
35 |
36 | requirements.forEach((requirement) => {
37 | if (!requirement.re.test(password)) {
38 | multiplier += 1;
39 | }
40 | });
41 |
42 | return Math.max(100 - (100 / (requirements.length + 1)) * multiplier, 0);
43 | }
44 |
45 | export function PasswordStrength({ value, setValue, setStrength, isSignin }) {
46 | const strength = getStrength(value);
47 | setStrength(strength);
48 |
49 | const checks = requirements.map((requirement, index) => (
50 |
55 | ));
56 |
57 | const bars = Array(4)
58 | .fill(0)
59 | .map((_, index) => (
60 |