├── .env.sample
├── .gitignore
├── README.md
├── index.html
├── package.json
├── pnpm-lock.yaml
├── src
├── API
│ └── firebase.js
├── App.css
├── App.jsx
├── components
│ ├── 404Page
│ │ └── index.jsx
│ ├── CreateFile
│ │ └── index.jsx
│ ├── CreateFolder
│ │ └── index.jsx
│ ├── Dashboard
│ │ ├── BreadCrum.js
│ │ │ └── index.jsx
│ │ ├── FileComponent
│ │ │ ├── FileComponent.css
│ │ │ ├── Header.jsx
│ │ │ └── index.jsx
│ │ ├── FolderAdminComponent
│ │ │ └── index.jsx
│ │ ├── FolderComponent
│ │ │ └── index.jsx
│ │ ├── Home
│ │ │ └── index.jsx
│ │ ├── NavDashboard
│ │ │ └── index.jsx
│ │ ├── SubNav.js
│ │ │ └── index.jsx
│ │ └── index.jsx
│ ├── DeleteButton
│ │ └── index.jsx
│ ├── Home
│ │ └── index.jsx
│ ├── Navbar
│ │ └── index.jsx
│ ├── UploadFile
│ │ └── index.jsx
│ └── authentication
│ │ ├── Login
│ │ └── index.jsx
│ │ └── Register
│ │ └── index.jsx
├── index.css
├── index.jsx
├── models
│ ├── docs.js
│ ├── files.js
│ └── users.js
└── redux
│ ├── actionCreators
│ ├── authActionCreators.js
│ └── filefoldersActionCreators.js
│ ├── actions
│ ├── authActions.js
│ └── filefoldersActions.js
│ └── reducers
│ ├── authReducer.js
│ └── filefolderReducer.js
└── vite.config.js
/.env.sample:
--------------------------------------------------------------------------------
1 | VITE_APP_apiKey = your_data
2 | VITE_APP_authDomain = your_data
3 | VITE_APP_projectId = your_data
4 | VITE_APP_storageBucket = your_data
5 | VITE_APP_messagingSenderId = your_data
6 | VITE_APP_appId = your_data
--------------------------------------------------------------------------------
/.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
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Js Firebase FIle Management System [V1.0]
2 |
3 | This is the file management system made with reactjs and frebase. where user cn upload files, create folders and sub folders, and also even user can create file and he/she can edit and save created file.
4 |
5 | **This is the upgraded version from CRA to VITE, React 18 and Firebase 10 (Tutorial of upgradation coming soon)**
6 |
7 | Demo Link Vercel
8 |
9 | ## Prerequities
10 | 1. Create Firsebase project , enable storage, Auth and Firebase Database
11 | 2. Create Web app and get credentials
12 | 3. Follow installation guide
13 |
14 | **note:** if you are getting any cors error from firebase then follow this link https://firebase.google.com/docs/storage/web/download-files#cors_configuration
15 |
16 |
17 | ## Installation Guide
18 |
19 | 1. Download repo and unzip it.
20 | 2. Open terminal under this repo and run `pnpm install` to install all dependencies
21 | 3. Now, create .env file in the root directory and copy the data from .env.sample file
22 | 4. Now, Create your firebase project on firebase console and fill your credentials in .env file
23 | 5. Now, run `pnpm start` to run development server.
24 |
25 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | File Management System
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "file-management-system-reactjs",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@fortawesome/fontawesome-svg-core": "^1.2.35",
7 | "@fortawesome/free-solid-svg-icons": "^5.15.3",
8 | "@fortawesome/react-fontawesome": "^0.1.14",
9 | "@testing-library/jest-dom": "^5.12.0",
10 | "@testing-library/react": "^11.2.6",
11 | "@testing-library/user-event": "^12.8.3",
12 | "@vitejs/plugin-react": "^4.2.1",
13 | "@vitejs/plugin-react-swc": "^3.6.0",
14 | "bootstrap": "^5.0.1",
15 | "codemirror": "^5.61.1",
16 | "firebase": "^10.9.0",
17 | "react": "^18.2.0",
18 | "react-bootstrap": "^1.6.0",
19 | "react-codemirror2": "^7.2.1",
20 | "react-dom": "^18.2.0",
21 | "react-file-viewer": "^1.2.1",
22 | "react-redux": "^7.2.4",
23 | "react-router-dom": "^5.2.0",
24 | "react-toastify": "^7.0.4",
25 | "redux": "^4.1.0",
26 | "redux-devtools-extension": "^2.13.9",
27 | "redux-thunk": "^2.3.0",
28 | "vite": "^5.1.6",
29 | "web-vitals": "^1.1.1"
30 | },
31 | "scripts": {
32 | "start": "vite",
33 | "build": "vite build",
34 | "preview": "vite preview"
35 | },
36 | "eslintConfig": {
37 | "extends": [
38 | "react-app",
39 | "react-app/jest"
40 | ]
41 | },
42 | "browserslist": {
43 | "production": [
44 | ">0.2%",
45 | "not dead",
46 | "not op_mini all"
47 | ],
48 | "development": [
49 | "last 1 chrome version",
50 | "last 1 firefox version",
51 | "last 1 safari version"
52 | ]
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/API/firebase.js:
--------------------------------------------------------------------------------
1 | import firebase from 'firebase/compat/app';
2 | import 'firebase/compat/auth';
3 | import 'firebase/compat/firestore';
4 | import 'firebase/compat/storage';
5 |
6 | const firebaseConfig = {
7 | apiKey: import.meta.env.VITE_APP_apiKey,
8 | authDomain: import.meta.env.VITE_APP_authDomain,
9 | projectId: import.meta.env.VITE_APP_projectId,
10 | storageBucket: import.meta.env.VITE_APP_storageBucket,
11 | messagingSenderId: import.meta.env.VITE_APP_messagingSenderId,
12 | appId: import.meta.env.VITE_APP_appId,
13 | };
14 |
15 | firebase.initializeApp(firebaseConfig);
16 |
17 | const firestore = firebase.firestore();
18 | export const database = {
19 | users: firestore.collection('users'),
20 | docs: firestore.collection('docs'),
21 | files: firestore.collection('files'),
22 | date: firebase.firestore.FieldValue.serverTimestamp(),
23 | };
24 |
25 | export const storage = firebase.storage();
26 |
27 | export const auth = firebase.auth();
28 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import { Route, Switch } from "react-router-dom";
3 | import { ToastContainer } from "react-toastify";
4 |
5 | import Register from "./components/authentication/Register";
6 | import Login from "./components/authentication/Login";
7 | import NavbarComponent from "./components/Navbar";
8 | import Dashboard from "./components/Dashboard";
9 |
10 | import "./App.css";
11 |
12 | import { useDispatch, useSelector } from "react-redux";
13 | import { getUser } from "./redux/actionCreators/authActionCreators";
14 |
15 | const App = () => {
16 | const dispatch = useDispatch();
17 | const isLoggedIn = useSelector((state) => state.auth.isLoggedIn);
18 |
19 | useEffect(() => {
20 | if (!isLoggedIn) {
21 | dispatch(getUser());
22 | }
23 | }, [dispatch]);
24 | return (
25 |
26 |
27 |
28 |
29 |
30 |
31 | Welcome to file management system
32 |
33 | }>
34 | }>
35 |
36 |
37 |
38 | );
39 | };
40 |
41 | export default App;
42 |
--------------------------------------------------------------------------------
/src/components/404Page/index.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Button, Col, Container, Row } from "react-bootstrap";
3 | import { Link } from "react-router-dom";
4 |
5 | const Page404 = () => {
6 | return (
7 |
8 |
9 |
10 |
11 | 404 Page Not Found
12 |
13 |
16 |
17 |
18 |
19 | );
20 | };
21 |
22 | export default Page404;
23 |
--------------------------------------------------------------------------------
/src/components/CreateFile/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { faFileAlt, faTimes } from "@fortawesome/free-solid-svg-icons";
3 | import { Button, Form, Modal } from "react-bootstrap";
4 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
5 | import { toast } from "react-toastify";
6 | import { shallowEqual, useDispatch, useSelector } from "react-redux";
7 | import { addFileUser } from "../../redux/actionCreators/filefoldersActionCreators";
8 |
9 | const CreateFile = ({ currentFolder }) => {
10 | const [showModal, setShowModal] = useState(false);
11 | const [file, setFile] = useState("");
12 |
13 | const dispatch = useDispatch();
14 | const { userId, userFiles } = useSelector(
15 | (state) => ({
16 | userId: state.auth.userId,
17 | userFiles: state.filefolders.userFiles,
18 | }),
19 | shallowEqual
20 | );
21 |
22 | const handleFileSubmit = (e) => {
23 | e.preventDefault();
24 |
25 | if (!file) return toast.dark("Please add file name!");
26 | const fileExtension =
27 | file.split(".").length > 1
28 | ? file.split(".")[file.split(".").length - 1].toLowerCase()
29 | : "txt";
30 | const allowedExtensions = [
31 | "html",
32 | "php",
33 | "js",
34 | "jsx",
35 | "txt",
36 | "xml",
37 | "css",
38 | "c",
39 | "cpp",
40 | "java",
41 | "cs",
42 | "py",
43 | "json",
44 | ];
45 |
46 | if (allowedExtensions.indexOf(fileExtension) === -1) {
47 | return toast.dark(`File with extension ${fileExtension} not allowed!`);
48 | }
49 | const fileName =
50 | file.split(".").length > 1 ? file : file + "." + fileExtension;
51 |
52 | const filteredFiles =
53 | currentFolder === "root folder"
54 | ? userFiles.filter(
55 | (file) =>
56 | file.data.parent === "" && file.data.name === fileName.trim()
57 | )
58 | : userFiles.filter(
59 | (file) =>
60 | file.data.parent === currentFolder.docId &&
61 | file.data.name === fileName.trim()
62 | );
63 |
64 | if (filteredFiles.length > 0)
65 | return toast.dark("This is alredy present in folder");
66 |
67 | if (currentFolder === "root folder") {
68 | dispatch(
69 | addFileUser({
70 | uid: userId,
71 | parent: "",
72 | data: "",
73 | name: fileName,
74 | url: "",
75 | path: [],
76 | })
77 | );
78 | setFile("");
79 | setShowModal(false);
80 | return;
81 | }
82 |
83 | const path =
84 | currentFolder.data.path.length > 0
85 | ? [
86 | ...currentFolder.data.path,
87 | { id: currentFolder.docId, name: currentFolder.data.name },
88 | ]
89 | : [{ id: currentFolder.docId, name: currentFolder.data.name }];
90 |
91 | dispatch(
92 | addFileUser({
93 | uid: userId,
94 | parent: currentFolder.docId,
95 | data: "",
96 | name: fileName,
97 | url: "",
98 | path: path,
99 | })
100 | );
101 | setFile("");
102 | setShowModal(false);
103 | return;
104 | };
105 |
106 | return (
107 | <>
108 | setShowModal(false)}>
109 |
110 | Create File
111 |
118 |
119 |
120 |
122 | setFile(e.target.value)}
127 | />
128 |
129 |
130 |
133 |
134 |
135 |
136 |
137 |
145 | >
146 | );
147 | };
148 |
149 | export default CreateFile;
150 |
--------------------------------------------------------------------------------
/src/components/CreateFolder/index.jsx:
--------------------------------------------------------------------------------
1 | import { faFolderPlus, faTimes } from "@fortawesome/free-solid-svg-icons";
2 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
3 | import React, { useState } from "react";
4 | import { Button, Form, Modal } from "react-bootstrap";
5 | import { shallowEqual, useDispatch, useSelector } from "react-redux";
6 | import { toast } from "react-toastify";
7 | import { addFolderUser } from "../../redux/actionCreators/filefoldersActionCreators";
8 |
9 | const CreateFolder = ({ currentFolder }) => {
10 | const [showModal, setShowModal] = useState(false);
11 | const [folderName, setFolderName] = useState("");
12 |
13 | const dispatch = useDispatch();
14 | const { userId, userFolders } = useSelector(
15 | (state) => ({
16 | userId: state.auth.userId,
17 | userFolders: state.filefolders.userFolders,
18 | }),
19 | shallowEqual
20 | );
21 |
22 | const handleFolderSubmit = (e) => {
23 | e.preventDefault();
24 | const filteredFolders =
25 | currentFolder === "root folder"
26 | ? userFolders.filter(
27 | (folder) =>
28 | folder.data.parent === "" &&
29 | folder.data.name === folderName.trim()
30 | )
31 | : userFolders.filter(
32 | (folder) =>
33 | folder.data.parent === currentFolder.docId &&
34 | folder.data.name === folderName.trim()
35 | );
36 | if (!folderName) return toast.dark("Please enter folder name!");
37 |
38 | if (filteredFolders.length > 0)
39 | return toast.dark("This is alredy present in folder");
40 |
41 | if (currentFolder === "root folder") {
42 | dispatch(addFolderUser(folderName, userId, "", []));
43 | setFolderName("");
44 | setShowModal(false);
45 | return;
46 | }
47 |
48 | const path =
49 | currentFolder.data.path.length > 0
50 | ? [
51 | ...currentFolder.data.path,
52 | { id: currentFolder.docId, name: currentFolder.data.name },
53 | ]
54 | : [{ id: currentFolder.docId, name: currentFolder.data.name }];
55 | dispatch(addFolderUser(folderName, userId, currentFolder.docId, path));
56 | setFolderName("");
57 | setShowModal(false);
58 | return;
59 | };
60 | return (
61 | <>
62 | setShowModal(false)}>
63 |
64 | Create Folder
65 |
72 |
73 |
74 |
76 | setFolderName(e.target.value)}
81 | />
82 |
83 |
84 |
87 |
88 |
89 |
90 |
91 |
99 | >
100 | );
101 | };
102 |
103 | export default CreateFolder;
104 |
--------------------------------------------------------------------------------
/src/components/Dashboard/BreadCrum.js/index.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Breadcrumb, Button } from "react-bootstrap";
3 | import { useHistory } from "react-router-dom";
4 |
5 | const BreadCrum = ({ currentFolder }) => {
6 | const history = useHistory();
7 | return (
8 |
9 | {currentFolder && currentFolder.data.path.length > 0 ? (
10 | <>
11 | history.push("/dashboard")}
18 | className="text-decoration-none"
19 | >
20 | Root
21 |
22 | {currentFolder.data.path.map((folder, index) => (
23 |
31 | history.push(
32 | currentFolder.data.createdBy === "admin"
33 | ? `/dashboard/folder/admin/${folder.id}`
34 | : `/dashboard/folder/${folder.id}`
35 | )
36 | }
37 | >
38 | {folder.name}
39 |
40 | ))}
41 |
42 | {currentFolder.data.name}
43 |
44 | >
45 | ) : (
46 | <>
47 | history.push("/dashboard")}
54 | >
55 | Root
56 |
57 |
58 | {currentFolder.data.name}
59 |
60 | >
61 | )}
62 |
63 | );
64 | };
65 |
66 | export default BreadCrum;
67 |
--------------------------------------------------------------------------------
/src/components/Dashboard/FileComponent/FileComponent.css:
--------------------------------------------------------------------------------
1 | .CodeMirror {
2 | text-align: left !important;
3 | width: 100vh !important;
4 | height: 100% !important;
5 | max-height: 100vh !important;
6 | }
7 | .CodeMirror-wrap pre {
8 | word-break: break-word;
9 | }
10 | .cm-wrap {
11 | height: 100vh !important;
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Dashboard/FileComponent/Header.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { faArrowLeft, faSave } from '@fortawesome/free-solid-svg-icons';
3 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
4 | import { Button, Col, Row } from 'react-bootstrap';
5 | import { useHistory } from 'react-router-dom';
6 | import { userFileDataUpdate } from '../../../redux/actionCreators/filefoldersActionCreators';
7 | import { useDispatch } from 'react-redux';
8 |
9 | const Header = ({ data, prevData, currentFile, setPrevData, setData }) => {
10 | const history = useHistory();
11 |
12 | const dispatch = useDispatch();
13 | const pushItBack = () => {
14 | if (data.trim() !== prevData.trim()) {
15 | if (window.confirm('are your sure to leave without saving data?')) {
16 | history.push(
17 | currentFile.data.parent === ''
18 | ? '/dashboard'
19 | : `/dashboard/folder/${currentFile.data.parent}`
20 | );
21 | } else {
22 | return;
23 | }
24 | } else {
25 | history.push(
26 | currentFile.data.parent === ''
27 | ? '/dashboard'
28 | : `/dashboard/folder/${currentFile.data.parent}`
29 | );
30 | }
31 | };
32 | const saveFile = () => {
33 | setData(data + '\n');
34 | setPrevData(data.trim());
35 | dispatch(userFileDataUpdate(data.trim(), currentFile.docId));
36 | };
37 |
38 | return (
39 |
40 |
41 |
42 | {currentFile.data.name}
43 | {data.trim() !== prevData.trim() && ' [* . Modified]'}
44 |
45 |
46 |
47 |
54 |
55 |
59 |
60 |
61 | );
62 | };
63 |
64 | export default Header;
65 |
--------------------------------------------------------------------------------
/src/components/Dashboard/FileComponent/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { Button, Col, Image, Row } from "react-bootstrap";
3 | import { shallowEqual, useDispatch, useSelector } from "react-redux";
4 | import { useHistory, useParams } from "react-router-dom";
5 | import {
6 | getAdminFiles,
7 | getAdminFolders,
8 | getUserFiles,
9 | getUserFolders,
10 | } from "../../../redux/actionCreators/filefoldersActionCreators";
11 | import "codemirror/lib/codemirror.css";
12 | import "codemirror/theme/eclipse.css";
13 | import "codemirror/mode/xml/xml";
14 | import "codemirror/mode/php/php";
15 | import "codemirror/mode/javascript/javascript";
16 | import "codemirror/mode/textile/textile";
17 | import "codemirror/mode/jsx/jsx";
18 | import "codemirror/mode/css/css";
19 | import "codemirror/mode/python/python";
20 | import "codemirror/mode/clike/clike";
21 | import { Controlled as CodeMirror } from "react-codemirror2";
22 | import FileViewer from "react-file-viewer";
23 |
24 | import "./FileComponent.css";
25 | import Header from "./Header";
26 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
27 | import { faDownload, faEye } from "@fortawesome/free-solid-svg-icons";
28 |
29 | const FileComponent = () => {
30 | const { fileId } = useParams();
31 | const history = useHistory();
32 |
33 | const { isLoading, userId, currentFile, folders } = useSelector(
34 | (state) => ({
35 | userId: state.auth.userId,
36 | isLoading: state.filefolders.isLoading,
37 | folders: state.filefolders.UserFolders,
38 | currentFile: state.filefolders.userFiles?.find(
39 | (file) => file.docId === fileId
40 | ),
41 | }),
42 | shallowEqual
43 | );
44 | const [prevData, setPrevData] = useState("");
45 | const [data, setData] = useState("");
46 |
47 | const codes = {
48 | html: "xml",
49 | php: "php",
50 | js: "javascript",
51 | jsx: "jsx",
52 | txt: "textile",
53 | xml: "xml",
54 | css: "css",
55 | c: "clike",
56 | cpp: "clike",
57 | java: "textile",
58 | cs: "clike",
59 | py: "python",
60 | json: "javascript",
61 | };
62 |
63 | const dispatch = useDispatch();
64 |
65 | const extension =
66 | currentFile &&
67 | currentFile.data.name.split(".")[
68 | currentFile.data.name.split(".").length - 1
69 | ];
70 | useEffect(() => {
71 | if (isLoading && !folders && !currentFile) {
72 | dispatch(getAdminFolders());
73 | dispatch(getAdminFiles());
74 | dispatch(getUserFolders(userId));
75 | dispatch(getUserFiles(userId));
76 | }
77 |
78 | if (
79 | currentFile &&
80 | (currentFile.data.url === "" ||
81 | !currentFile.data.name.includes(".jpg") ||
82 | !currentFile.data.name.includes(".png") ||
83 | !currentFile.data.name.includes(".jpeg") ||
84 | !currentFile.data.name.includes(".doc") ||
85 | !currentFile.data.name.includes(".ppt") ||
86 | !currentFile.data.name.includes(".pptx") ||
87 | !currentFile.data.name.includes(".xls") ||
88 | !currentFile.data.name.includes(".rar"))
89 | ) {
90 | setData(currentFile.data.data);
91 | setPrevData(currentFile.data.data);
92 | }
93 | }, [dispatch, isLoading, currentFile && currentFile.data.data]);
94 |
95 | if (isLoading) {
96 | return (
97 |
98 |
99 | Fetching file...
100 |
101 |
102 | );
103 | }
104 | return (
105 | <>
106 | {currentFile ? (
107 | currentFile.data.url === "" &&
108 | (!currentFile.data.name.includes(".jpg") ||
109 | !currentFile.data.name.includes(".png") ||
110 | !currentFile.data.name.includes(".jpeg") ||
111 | !currentFile.data.name.includes(".doc") ||
112 | !currentFile.data.name.includes(".ppt") ||
113 | !currentFile.data.name.includes(".pptx") ||
114 | !currentFile.data.name.includes(".xls") ||
115 | !currentFile.data.name.includes(".rar")) ? (
116 | <>
117 |
125 |
129 |
130 | {
144 | setData(value);
145 | }}
146 | style={{ textAlign: "left !important" }}
147 | />
148 |
149 |
150 | >
151 | ) : currentFile.data.name
152 | .split(".")
153 | [currentFile.data.name.split(".").length - 1].includes("png") ||
154 | currentFile.data.name
155 | .split(".")
156 | [currentFile.data.name.split(".").length - 1].includes("jpg") ||
157 | currentFile.data.name
158 | .split(".")
159 | [currentFile.data.name.split(".").length - 1].includes("jpeg") ||
160 | currentFile.data.name
161 | .split(".")
162 | [currentFile.data.name.split(".").length - 1].includes("svg") ||
163 | currentFile.data.name
164 | .split(".")
165 | [currentFile.data.name.split(".").length - 1].includes("gif") ? (
166 |
170 |
171 |
175 |
179 | {currentFile.data.name}
180 |
181 |
199 |
200 |
201 |
208 |
209 |
210 |
211 | ) : (
212 |
216 |
217 |
221 |
225 | {currentFile.data.name}
226 |
227 |
245 |
246 |
247 | {currentFile.data.name
248 | .split(".")
249 | [currentFile.data.name.split(".").length - 1].includes(
250 | "pdf"
251 | ) ? (
252 |
257 |
258 | View{" "}
259 | {
260 | currentFile.data.name.split(".")[
261 | currentFile.data.name.split(".").length - 1
262 | ]
263 | }{" "}
264 | file
265 |
266 | ) : currentFile.data.name
267 | .split(".")
268 | [currentFile.data.name.split(".").length - 1].includes(
269 | "csv"
270 | ) ||
271 | currentFile.data.name
272 | .split(".")
273 | [currentFile.data.name.split(".").length - 1].includes(
274 | "xslx"
275 | ) ||
276 | currentFile.data.name
277 | .split(".")
278 | [currentFile.data.name.split(".").length - 1].includes(
279 | "docx"
280 | ) ||
281 | currentFile.data.name
282 | .split(".")
283 | [currentFile.data.name.split(".").length - 1].includes(
284 | "mp4"
285 | ) ||
286 | currentFile.data.name
287 | .split(".")
288 | [currentFile.data.name.split(".").length - 1].includes(
289 | "webm"
290 | ) ||
291 | currentFile.data.name
292 | .split(".")
293 | [currentFile.data.name.split(".").length - 1].includes(
294 | "mp3"
295 | ) ? (
296 |
305 | This file is not viewable
306 |
312 |
313 | Download
314 |
315 | >
316 | }
317 | style={{ height: "100%", width: "100%" }}
318 | />
319 | ) : (
320 |
325 |
326 | View{" "}
327 | {
328 | currentFile.data.name.split(".")[
329 | currentFile.data.name.split(".").length - 1
330 | ]
331 | }{" "}
332 | file
333 |
334 | )}
335 |
336 |
337 |
338 | )
339 | ) : (
340 |
341 | File Not Present
342 |
343 | )}
344 | >
345 | );
346 | };
347 |
348 | export default FileComponent;
349 |
--------------------------------------------------------------------------------
/src/components/Dashboard/FolderAdminComponent/index.jsx:
--------------------------------------------------------------------------------
1 | import { faFile } from '@fortawesome/free-solid-svg-icons';
2 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
3 | import React, { useEffect } from 'react';
4 | import { Col, Row } from 'react-bootstrap';
5 | import { shallowEqual, useDispatch, useSelector } from 'react-redux';
6 | import { useParams } from 'react-router-dom';
7 | import {
8 | getAdminFiles,
9 | getAdminFolders,
10 | } from '../../../redux/actionCreators/filefoldersActionCreators.js';
11 | import SubNav from '../SubNav.js/index.jsx';
12 |
13 | const FolderAdminComponent = () => {
14 | const { folderId } = useParams();
15 |
16 | const { files, folders, isLoading } = useSelector(
17 | (state) => ({
18 | folders: state.filefolders.adminFolders,
19 | files: state.filefolders.adminFiles,
20 | isLoading: state.filefolders.isLoading,
21 | }),
22 | shallowEqual
23 | );
24 | const dispatch = useDispatch();
25 |
26 | useEffect(() => {
27 | if (isLoading && (!folders || !files)) {
28 | dispatch(getAdminFolders());
29 | dispatch(getAdminFiles());
30 | }
31 | }, [dispatch, isLoading]);
32 | const adminFiles =
33 | files && files.filter((file) => file.data.parent === folderId);
34 |
35 | const currentFolder =
36 | folders && folders.find((folder) => folder.docId === folderId);
37 | if (isLoading) {
38 | return (
39 |
40 |
41 | Fetching data...
42 |
43 |
44 | );
45 | }
46 | return (
47 | <>
48 |
49 |
50 |
51 | Created Files
52 |
53 | {!files ? (
54 |
Fetching Files....
55 | ) : (
56 | adminFiles.map(({ data, docId }) => (
57 |
{
59 | if (e.currentTarget.classList.contains('text-white')) {
60 | e.currentTarget.style.background = '#fff';
61 | e.currentTarget.classList.remove('text-white');
62 | e.currentTarget.classList.remove('shadow-sm');
63 | } else {
64 | e.currentTarget.style.background = '#017bf562';
65 | e.currentTarget.classList.add('text-white');
66 | e.currentTarget.classList.add('shadow-sm');
67 | }
68 | }}
69 | key={docId}
70 | md={2}
71 | className="border h-100 mr-2 d-flex align-items-center justify-content-around flex-column py-1 rounded-2">
72 |
77 |
{data.name}
78 |
79 | ))
80 | )}
81 |
82 |
83 |
84 | >
85 | );
86 | };
87 |
88 | export default FolderAdminComponent;
89 |
--------------------------------------------------------------------------------
/src/components/Dashboard/FolderComponent/index.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | faFileAlt,
3 | faFileAudio,
4 | faFileImage,
5 | faFileVideo,
6 | faFolder,
7 | } from '@fortawesome/free-solid-svg-icons';
8 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
9 | import React, { useEffect } from 'react';
10 | import { Col, Row } from 'react-bootstrap';
11 | import { shallowEqual, useDispatch, useSelector } from 'react-redux';
12 | import { useHistory, useParams } from 'react-router-dom';
13 | import {
14 | getAdminFiles,
15 | getAdminFolders,
16 | getUserFiles,
17 | getUserFolders,
18 | selectItem,
19 | deselectItem,
20 | deselectAll,
21 | deselctFolder,
22 | selectFolder,
23 | } from '../../../redux/actionCreators/filefoldersActionCreators.js';
24 | import SubNav from '../SubNav.js/index.jsx';
25 |
26 | const FolderComponent = () => {
27 | const { folderId } = useParams();
28 |
29 | const { folders, isLoading, userId, files, selectedItems } = useSelector(
30 | (state) => ({
31 | folders: state.filefolders.userFolders,
32 | files: state.filefolders.userFiles,
33 | isLoading: state.filefolders.isLoading,
34 | userId: state.auth.userId,
35 | selectedItems: state.filefolders.selectedItems,
36 | }),
37 | shallowEqual
38 | );
39 | const dispatch = useDispatch();
40 | const history = useHistory();
41 |
42 | useEffect(() => {
43 | if (isLoading) {
44 | dispatch(getAdminFolders());
45 | dispatch(getAdminFiles());
46 | }
47 | if (!folders && !files) {
48 | dispatch(getUserFolders(userId));
49 | dispatch(getUserFiles(userId));
50 | }
51 | }, [dispatch, folders, isLoading]);
52 | const userFolders =
53 | folders && folders.filter((file) => file.data.parent === folderId);
54 |
55 | const currentFolder =
56 | folders && folders.find((folder) => folder.docId === folderId);
57 |
58 | const createdFiles =
59 | files &&
60 | files.filter(
61 | (file) => file.data.parent === folderId && file.data.url === ''
62 | );
63 |
64 | const uploadedFiles =
65 | files &&
66 | files.filter(
67 | (file) => file.data.parent === folderId && file.data.url !== ''
68 | );
69 |
70 | const isItemSelected = (docId) => {
71 | return selectedItems.find((item) => item.docId === docId) ? true : false;
72 | };
73 |
74 | const changeRoute = (url) => {
75 | dispatch(deselectAll());
76 | history.push(url);
77 | };
78 |
79 | if (isLoading) {
80 | return (
81 |
82 |
83 | Fetching data...
84 |
85 |
86 | );
87 | }
88 |
89 | if (
90 | userFolders &&
91 | userFolders.length < 1 &&
92 | createdFiles &&
93 | createdFiles.length < 1 &&
94 | uploadedFiles &&
95 | uploadedFiles.length < 1
96 | ) {
97 | return (
98 | <>
99 |
100 |
101 |
102 | Empty Folder
103 |
104 |
105 | >
106 | );
107 | }
108 | return (
109 | <>
110 |
111 | {userFolders && userFolders.length > 0 && (
112 | <>
113 | Created Folders
114 |
118 | {!folders ? (
119 | Fetching Files....
120 | ) : (
121 | userFolders.map(({ data, docId }) => (
122 |
124 | changeRoute(`/dashboard/folder/${docId}`)
125 | }
126 | onClick={(e) => {
127 | if (isItemSelected(docId)) {
128 | dispatch(deselctFolder(docId));
129 | e.currentTarget.style.background = '#fff';
130 | e.currentTarget.classList.remove('text-white');
131 | e.currentTarget.classList.remove('shadow-sm');
132 | } else {
133 | // dispatch(selectItem({ docId, data, type: 'folder' }));
134 | dispatch(selectFolder({ docId, data }));
135 | e.currentTarget.style.background = '#017bf562';
136 | e.currentTarget.classList.add('text-white');
137 | e.currentTarget.classList.add('shadow-sm');
138 | }
139 | }}
140 | key={docId}
141 | md={2}
142 | className="border h-100 mr-2 d-flex align-items-center justify-content-around flex-column py-1 rounded-2">
143 |
148 | {data.name}
149 |
150 | ))
151 | )}
152 |
153 | >
154 | )}
155 | {createdFiles && createdFiles.length > 0 && (
156 | <>
157 | Created Files
158 |
162 | {createdFiles.map(({ data, docId }) => (
163 | changeRoute(`/dashboard/file/${docId}`)}
165 | onClick={(e) => {
166 | if (isItemSelected(docId)) {
167 | dispatch(deselectItem({ docId }));
168 | e.currentTarget.style.background = '#fff';
169 | e.currentTarget.classList.remove('text-white');
170 | e.currentTarget.classList.remove('shadow-sm');
171 | } else {
172 | dispatch(selectItem({ docId, data, type: 'file' }));
173 | e.currentTarget.style.background = '#017bf562';
174 | e.currentTarget.classList.add('text-white');
175 | e.currentTarget.classList.add('shadow-sm');
176 | }
177 | }}
178 | key={docId}
179 | md={2}
180 | className="border h-100 mr-2 d-flex align-items-center justify-content-around flex-column py-1 rounded-2">
181 |
186 | {data.name}
187 |
188 | ))}
189 |
190 | >
191 | )}
192 | {uploadedFiles && uploadedFiles.length > 0 && (
193 | <>
194 | Uploaded Files
195 |
199 | {uploadedFiles.map(({ data, docId }) => (
200 | changeRoute(`/dashboard/file/${docId}`)}
202 | onClick={(e) => {
203 | if (isItemSelected(docId)) {
204 | dispatch(deselectItem({ docId }));
205 | e.currentTarget.style.background = '#fff';
206 | e.currentTarget.classList.remove('text-white');
207 | e.currentTarget.classList.remove('shadow-sm');
208 | } else {
209 | dispatch(selectItem({ docId, data, type: 'file' }));
210 | e.currentTarget.style.background = '#017bf562';
211 | e.currentTarget.classList.add('text-white');
212 | e.currentTarget.classList.add('shadow-sm');
213 | }
214 | }}
215 | key={docId}
216 | md={2}
217 | className="border h-100 mr-2 d-flex align-items-center justify-content-around flex-column py-1 rounded-2">
218 |
252 | {data.name}
253 |
254 | ))}
255 |
256 | >
257 | )}
258 | >
259 | );
260 | };
261 |
262 | export default FolderComponent;
263 |
--------------------------------------------------------------------------------
/src/components/Dashboard/Home/index.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | faFileImage,
3 | faFileAlt,
4 | faFileAudio,
5 | faFileVideo,
6 | faFolder,
7 | } from '@fortawesome/free-solid-svg-icons';
8 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
9 | import React, { useEffect } from 'react';
10 | import { Col, Row } from 'react-bootstrap';
11 | import { shallowEqual, useDispatch, useSelector } from 'react-redux';
12 | import { useHistory } from 'react-router-dom';
13 | import {
14 | getAdminFiles,
15 | getAdminFolders,
16 | getUserFiles,
17 | getUserFolders,
18 | selectItem,
19 | deselctFolder,
20 | deselectItem,
21 | deselectAll,
22 | selectFolder,
23 | } from '../../../redux/actionCreators/filefoldersActionCreators.js';
24 | import SubNav from '../SubNav.js/index.jsx';
25 |
26 | const Home = () => {
27 | const history = useHistory();
28 | const dispatch = useDispatch();
29 | const {
30 | isLoading,
31 | adminFolders,
32 | allUserFolders,
33 | userId,
34 | allUserFiles,
35 | selectedItems,
36 | } = useSelector(
37 | (state) => ({
38 | isLoading: state.filefolders.isLoading,
39 | adminFolders: state.filefolders.adminFolders,
40 | allUserFolders: state.filefolders.userFolders,
41 | allUserFiles: state.filefolders.userFiles,
42 | userId: state.auth.userId,
43 | selectedItems: state.filefolders.selectedItems,
44 | }),
45 | shallowEqual
46 | );
47 |
48 | const isItemSelected = (docId) => {
49 | return selectedItems.find((item) => item.docId === docId) ? true : false;
50 | };
51 |
52 | const changeRoute = (url) => {
53 | dispatch(deselectAll());
54 | history.push(url);
55 | };
56 |
57 | const userFolders =
58 | allUserFolders &&
59 | allUserFolders.filter((folder) => folder.data.parent === '');
60 |
61 | const createdUserFiles =
62 | allUserFiles &&
63 | allUserFiles.filter(
64 | (file) => file.data.parent === '' && file.data.url === ''
65 | );
66 | const uploadedUserFiles =
67 | allUserFiles &&
68 | allUserFiles.filter(
69 | (file) => file.data.parent === '' && file.data.url !== ''
70 | );
71 |
72 | useEffect(() => {
73 | if (isLoading && !adminFolders) {
74 | dispatch(getAdminFolders());
75 | dispatch(getAdminFiles());
76 | }
77 | if (!userFolders) {
78 | dispatch(getUserFiles(userId));
79 | dispatch(getUserFolders(userId));
80 | }
81 | }, [dispatch, isLoading]);
82 |
83 | if (isLoading) {
84 | return (
85 |
86 |
87 | Fetching folders...
88 |
89 |
90 | );
91 | }
92 |
93 | return (
94 | <>
95 |
96 | {adminFolders && adminFolders.length > 0 && (
97 | <>
98 | Admin Folders
99 |
100 | {adminFolders.map(({ data, docId }) => (
101 |
103 | history.push(`/dashboard/folder/admin/${docId}`)
104 | }
105 | onClick={(e) => {
106 | if (e.currentTarget.classList.contains('text-white')) {
107 | e.currentTarget.style.background = '#fff';
108 | e.currentTarget.classList.remove('text-white');
109 | e.currentTarget.classList.remove('shadow-sm');
110 | } else {
111 | e.currentTarget.style.background = '#017bf562';
112 | e.currentTarget.classList.add('text-white');
113 | e.currentTarget.classList.add('shadow-sm');
114 | }
115 | }}
116 | key={docId}
117 | md={2}
118 | className="border h-100 d-flex align-items-center justify-content-around flex-column py-1 rounded-2">
119 |
124 | {data.name}
125 |
126 | ))}
127 |
128 | >
129 | )}
130 | {userFolders && userFolders.length > 0 && (
131 | <>
132 | Created Folders
133 |
134 | {userFolders.map(({ data, docId }) => (
135 | changeRoute(`/dashboard/folder/${docId}`)}
137 | onClick={(e) => {
138 | if (isItemSelected(docId)) {
139 | dispatch(deselctFolder(docId));
140 | e.currentTarget.style.background = '#fff';
141 | e.currentTarget.classList.remove('text-white');
142 | e.currentTarget.classList.remove('shadow-sm');
143 | } else {
144 | // dispatch(selectItem({ docId, data, type: 'folder' }));
145 | dispatch(selectFolder({ docId, data }));
146 | e.currentTarget.style.background = '#017bf562';
147 | e.currentTarget.classList.add('text-white');
148 | e.currentTarget.classList.add('shadow-sm');
149 | }
150 | }}
151 | key={docId}
152 | md={2}
153 | className="border h-100 d-flex align-items-center justify-content-around flex-column py-1 rounded-2">
154 |
159 | {data.name}
160 |
161 | ))}
162 |
163 | >
164 | )}
165 | {createdUserFiles && createdUserFiles.length > 0 && (
166 | <>
167 | Created Files
168 |
169 | {createdUserFiles.map(({ data, docId }) => (
170 | changeRoute(`/dashboard/file/${docId}`)}
172 | onClick={(e) => {
173 | if (isItemSelected(docId)) {
174 | dispatch(deselectItem({ docId }));
175 | e.currentTarget.style.background = '#fff';
176 | e.currentTarget.classList.remove('text-white');
177 | e.currentTarget.classList.remove('shadow-sm');
178 | } else {
179 | dispatch(selectItem({ docId, data, type: 'file' }));
180 | e.currentTarget.style.background = '#017bf562';
181 | e.currentTarget.classList.add('text-white');
182 | e.currentTarget.classList.add('shadow-sm');
183 | }
184 | }}
185 | key={docId}
186 | md={2}
187 | className="border h-100 d-flex align-items-center justify-content-around flex-column py-1 rounded-2">
188 |
193 | {data.name}
194 |
195 | ))}
196 |
197 | >
198 | )}
199 | {uploadedUserFiles && uploadedUserFiles.length > 0 && (
200 | <>
201 | Uploaded Files
202 |
206 | {uploadedUserFiles.map(({ data, docId }) => (
207 | changeRoute(`/dashboard/file/${docId}`)}
209 | onClick={(e) => {
210 | if (isItemSelected(docId)) {
211 | dispatch(deselectItem({ docId }));
212 | e.currentTarget.style.background = '#fff';
213 | e.currentTarget.classList.remove('text-white');
214 | e.currentTarget.classList.remove('shadow-sm');
215 | } else {
216 | dispatch(selectItem({ docId, data, type: 'file' }));
217 | e.currentTarget.style.background = '#017bf562';
218 | e.currentTarget.classList.add('text-white');
219 | e.currentTarget.classList.add('shadow-sm');
220 | }
221 | }}
222 | key={docId}
223 | md={2}
224 | className="border h-100 mr-2 d-flex align-items-center justify-content-around flex-column py-1 rounded-2">
225 |
259 | {data.name}
260 |
261 | ))}
262 |
263 | >
264 | )}
265 | >
266 | );
267 | };
268 |
269 | export default Home;
270 |
--------------------------------------------------------------------------------
/src/components/Dashboard/NavDashboard/index.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Button, Nav, Navbar } from "react-bootstrap";
3 | import { shallowEqual, useDispatch, useSelector } from "react-redux";
4 | import { Link, useHistory } from "react-router-dom";
5 | import { logoutUser } from "../../../redux/actionCreators/authActionCreators";
6 |
7 | const NavDashboard = () => {
8 | const history = useHistory();
9 | const dispatch = useDispatch();
10 |
11 | const { isLoggedIn, user } = useSelector(
12 | (state) => ({
13 | isLoggedIn: state.auth.isLoggedIn,
14 | user: state.auth.user,
15 | }),
16 | shallowEqual
17 | );
18 |
19 | const logout = () => {
20 | dispatch(logoutUser());
21 | };
22 |
23 | return (
24 |
30 |
35 | File Management System
36 |
37 |
85 |
86 | );
87 | };
88 |
89 | export default NavDashboard;
90 |
--------------------------------------------------------------------------------
/src/components/Dashboard/SubNav.js/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Col } from 'react-bootstrap';
3 | import CreateFile from '../../CreateFile/index.jsx';
4 | import CreateFolder from '../../CreateFolder/index.jsx';
5 | import UploadFile from '../../UploadFile/index.jsx';
6 | import BreadCrum from '../BreadCrum.js/index.jsx';
7 | import DeleteButton from '../../DeleteButton/index.jsx';
8 |
9 | const SubNav = ({ currentFolder }) => {
10 | return (
11 |
14 | {currentFolder && currentFolder !== 'root folder' ? (
15 | <>
16 |
17 | {currentFolder.data.createdBy !== 'admin' && (
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | )}
28 | >
29 | ) : (
30 | <>
31 | Root
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | >
42 | )}
43 |
44 | );
45 | };
46 |
47 | export default SubNav;
48 |
--------------------------------------------------------------------------------
/src/components/Dashboard/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import { Container } from "react-bootstrap";
3 | import { shallowEqual, useSelector } from "react-redux";
4 | import { Route, Switch, useHistory, useRouteMatch } from "react-router-dom";
5 |
6 | import NavDashboard from "./NavDashboard";
7 | import Home from "./Home";
8 | import FolderAdminComponent from "./FolderAdminComponent";
9 | import FolderComponent from "./FolderComponent";
10 | import FileComponent from "./FileComponent";
11 |
12 | const Dashboard = () => {
13 | const history = useHistory();
14 | const { path } = useRouteMatch();
15 |
16 | const { isLoggedIn } = useSelector(
17 | (state) => ({
18 | isLoggedIn: state.auth.isLoggedIn,
19 | }),
20 | shallowEqual
21 | );
22 | useEffect(() => {
23 | if (!isLoggedIn) {
24 | history.push("/login");
25 | }
26 | }, [isLoggedIn]);
27 | return (
28 |
29 |
30 |
31 |
32 |
37 |
42 |
43 |
44 |
45 | );
46 | };
47 |
48 | export default Dashboard;
49 |
--------------------------------------------------------------------------------
/src/components/DeleteButton/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { faTrashAlt } from '@fortawesome/free-solid-svg-icons';
3 | import { Button } from 'react-bootstrap';
4 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
5 | import { shallowEqual, useDispatch, useSelector } from 'react-redux';
6 | import { deleteItems } from '../../redux/actionCreators/filefoldersActionCreators';
7 |
8 | const DeleteButton = ({ currentFolder }) => {
9 | const dispatch = useDispatch();
10 | const { userId, selectedItems } = useSelector(
11 | (state) => ({
12 | userId: state.auth.userId,
13 | selectedItems: state.filefolders.selectedItems,
14 | }),
15 | shallowEqual
16 | );
17 |
18 | const handleFileSubmit = () => {
19 | dispatch(deleteItems());
20 | };
21 |
22 | if (selectedItems.length === 0) {
23 | return null;
24 | }
25 |
26 | return (
27 |
34 | );
35 | };
36 |
37 | export default DeleteButton;
38 |
--------------------------------------------------------------------------------
/src/components/Home/index.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Container } from "react-bootstrap";
3 |
4 | const Home = () => {
5 | return ;
6 | };
7 |
8 | export default Home;
9 |
--------------------------------------------------------------------------------
/src/components/Navbar/index.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Button, Nav, Navbar } from "react-bootstrap";
3 | import { shallowEqual, useDispatch, useSelector } from "react-redux";
4 | import { Link, useHistory } from "react-router-dom";
5 | import { logoutUser } from "../../redux/actionCreators/authActionCreators";
6 |
7 | const NavbarComponent = () => {
8 | const history = useHistory();
9 | const dispatch = useDispatch();
10 |
11 | const { isLoggedIn, user } = useSelector(
12 | (state) => ({
13 | isLoggedIn: state.auth.isLoggedIn,
14 | user: state.auth.user,
15 | }),
16 | shallowEqual
17 | );
18 |
19 | const logout = () => {
20 | dispatch(logoutUser());
21 | };
22 |
23 | return (
24 |
25 |
30 | File Management System
31 |
32 |
94 |
95 | );
96 | };
97 |
98 | export default NavbarComponent;
99 |
--------------------------------------------------------------------------------
/src/components/UploadFile/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { faFileUpload, faTimes } from "@fortawesome/free-solid-svg-icons";
3 | import { Button, Form, Modal, ProgressBar } from "react-bootstrap";
4 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
5 | import { toast } from "react-toastify";
6 | import { shallowEqual, useDispatch, useSelector } from "react-redux";
7 | import { storage } from "../../API/firebase";
8 | import { addFileUser } from "../../redux/actionCreators/filefoldersActionCreators";
9 |
10 | const UploadFile = ({ currentFolder }) => {
11 | const [showModal, setShowModal] = useState(false);
12 | const [file, setFile] = useState(null);
13 | const [fileName, setFileName] = useState("");
14 | const [progress, setProgress] = useState(0);
15 |
16 | const dispatch = useDispatch();
17 | const { userId, userFiles } = useSelector(
18 | (state) => ({
19 | userId: state.auth.userId,
20 | userFiles: state.filefolders.userFiles,
21 | }),
22 | shallowEqual
23 | );
24 |
25 | const handleFileSubmit = (e) => {
26 | e.preventDefault();
27 | if (!file) return toast.dark("Please add file name!");
28 | const fileExtension = file.name.split(".").reverse()[0];
29 | const allowedExtensions = [
30 | "html",
31 | "php",
32 | "js",
33 | "jsx",
34 | "txt",
35 | "xml",
36 | "css",
37 | "c",
38 | "cpp",
39 | "java",
40 | "cs",
41 | "py",
42 | "json",
43 | "ppt",
44 | "pptx",
45 | "docx",
46 | "png",
47 | "jpg",
48 | "jpeg",
49 | "gif",
50 | "svg",
51 | "mp3",
52 | "mp4",
53 | "webm",
54 | "pdf",
55 | ];
56 |
57 | if (allowedExtensions.indexOf(fileExtension) === -1) {
58 | return toast.dark(`File with extension ${fileExtension} not allowed!`);
59 | }
60 | const filteredFiles =
61 | currentFolder === "root folder"
62 | ? userFiles.filter(
63 | (file) =>
64 | file.data.parent === "" &&
65 | file.data.name === fileName.split("\\").reverse()[0]
66 | )
67 | : userFiles.filter(
68 | (file) =>
69 | file.data.parent === currentFolder.docId &&
70 | file.data.name === fileName.split("\\").reverse()[0]
71 | );
72 | if (filteredFiles.length > 0)
73 | return toast.dark("This is alredy present in folder");
74 |
75 | const uploadFileRef = storage.ref(`files/${userId}/${file.name}`);
76 |
77 | uploadFileRef.put(file).on(
78 | "state_change",
79 | (snapshot) => {
80 | const newProgress =
81 | (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
82 | setProgress(newProgress);
83 | },
84 | (error) => {
85 | return toast.error(error.message);
86 | },
87 | async () => {
88 | const url = await uploadFileRef.getDownloadURL();
89 | if (currentFolder === "root folder") {
90 | dispatch(
91 | addFileUser({
92 | uid: userId,
93 | parent: "",
94 | data: "",
95 | name: file.name,
96 | url: url,
97 | path: [],
98 | })
99 | );
100 | setFile("");
101 | setProgress(0);
102 | setShowModal(false);
103 | return;
104 | }
105 |
106 | const path =
107 | currentFolder.data.path.length > 0
108 | ? [
109 | ...currentFolder.data.path,
110 | { id: currentFolder.docId, name: currentFolder.data.name },
111 | ]
112 | : [{ id: currentFolder.docId, name: currentFolder.data.name }];
113 |
114 | dispatch(
115 | addFileUser({
116 | uid: userId,
117 | parent: currentFolder.docId,
118 | data: "",
119 | name: file.name,
120 | url: url,
121 | path: path,
122 | })
123 | );
124 | setFile("");
125 | setProgress(0);
126 | setShowModal(false);
127 | return;
128 | }
129 | );
130 | };
131 |
132 | return (
133 | <>
134 | setShowModal(false)}>
135 |
136 |
137 | {progress && progress !== 100
138 | ? "Uploading..."
139 | : progress === 100
140 | ? "Uploaded"
141 | : "Upload File"}
142 |
143 |
150 |
151 |
152 | {progress && progress !== 100 ? (
153 |
154 | ) : progress === 100 ? (
155 | File Uploaded Successfully
156 | ) : (
157 |
159 | {
163 | setFileName(e.target.value);
164 | setFile(e.target.files[0]);
165 | }}
166 | custom="true"
167 | />
168 |
169 |
170 |
177 |
178 |
179 | )}
180 |
181 |
182 |
190 | >
191 | );
192 | };
193 |
194 | export default UploadFile;
195 |
--------------------------------------------------------------------------------
/src/components/authentication/Login/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { Button, Col, Container, Form, Row } from "react-bootstrap";
3 | import { useDispatch, useSelector } from "react-redux";
4 | import { Link, useHistory, useLocation } from "react-router-dom";
5 | import { toast } from "react-toastify";
6 | import { loginUser } from "../../../redux/actionCreators/authActionCreators";
7 |
8 | const Login = () => {
9 | const [email, setEmail] = useState("");
10 | const [password, setPassword] = useState("");
11 | const [error, setError] = useState("");
12 |
13 | const isLoggedIn = useSelector((state) => state.auth.isLoggedIn);
14 | const dispatch = useDispatch();
15 | const history = useHistory();
16 | const { pathname } = useLocation();
17 |
18 | const handleSubmit = (e) => {
19 | e.preventDefault();
20 |
21 | if (!email || !password) return toast.dark("Please fill in all fields!");
22 | const data = {
23 | email,
24 | password,
25 | };
26 | dispatch(loginUser(data, setError));
27 | };
28 |
29 | useEffect(() => {
30 | if (error) {
31 | toast.error(error);
32 | }
33 | if (isLoggedIn) {
34 | history.goBack();
35 | }
36 | }, [error]);
37 | return (
38 |
39 |
40 |
41 | Login
42 |
43 |
44 |
46 | setEmail(e.target.value)}
51 | />
52 |
53 |
54 | setPassword(e.target.value)}
59 | />
60 |
61 |
62 |
70 |
71 |
72 | Not a Member?
73 |
74 | Register
75 |
76 |
77 |
78 |
79 |
80 |
81 | );
82 | };
83 |
84 | export default Login;
85 |
--------------------------------------------------------------------------------
/src/components/authentication/Register/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { Button, Col, Container, Form, Row } from "react-bootstrap";
3 | import { useDispatch, useSelector } from "react-redux";
4 | import { Link, useHistory } from "react-router-dom";
5 | import { toast } from "react-toastify";
6 | import { registerUser } from "../../../redux/actionCreators/authActionCreators";
7 |
8 | const Register = () => {
9 | const [name, setName] = useState("");
10 | const [email, setEmail] = useState("");
11 | const [password, setPassword] = useState("");
12 | const [confirmPassword, setConfirmPassword] = useState("");
13 | const [error, setError] = useState("");
14 |
15 | const isLoggedIn = useSelector((state) => state.auth.isLoggedIn);
16 | const dispatch = useDispatch();
17 | const history = useHistory();
18 |
19 | const handleSubmit = (e) => {
20 | e.preventDefault();
21 |
22 | if (!name || !email || !password)
23 | return toast.dark("Please fill in all fields!");
24 |
25 | if (password !== confirmPassword)
26 | return toast.dark("Passwords donot match!");
27 |
28 | if (password.length < 8) {
29 | return toast.dark("Password must be of length 8 or more");
30 | }
31 | if (
32 | !/^(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{6,16}$/.test(password)
33 | ) {
34 | return toast.dark(
35 | "Password must have alteast a number and a special character!"
36 | );
37 | }
38 |
39 | const data = {
40 | name,
41 | email,
42 | password,
43 | };
44 |
45 | dispatch(registerUser(data, setError));
46 | };
47 |
48 | useEffect(() => {
49 | if (error) {
50 | toast.error(error);
51 | }
52 | if (isLoggedIn) {
53 | history.push("/dashboard");
54 | }
55 | }, [error, isLoggedIn]);
56 | return (
57 |
58 |
59 |
60 | Register
61 |
62 |
63 |
65 | setName(e.target.value)}
70 | />
71 |
72 |
73 | setEmail(e.target.value)}
78 | />
79 |
80 |
81 | setPassword(e.target.value)}
86 | />
87 |
88 |
89 | setConfirmPassword(e.target.value)}
94 | />
95 |
96 |
97 |
105 |
106 |
107 | Already a Member?
108 |
109 | Login
110 |
111 |
112 |
113 |
114 |
115 |
116 | );
117 | };
118 |
119 | export default Register;
120 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/src/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import { BrowserRouter as Router } from 'react-router-dom';
4 |
5 | import App from './App';
6 |
7 | import 'bootstrap/dist/css/bootstrap.min.css';
8 | import 'react-toastify/dist/ReactToastify.css';
9 | import './index.css';
10 |
11 | //redux
12 | import { applyMiddleware, combineReducers, createStore } from 'redux';
13 | import { composeWithDevTools } from 'redux-devtools-extension';
14 | import thunk from 'redux-thunk';
15 | import { Provider } from 'react-redux';
16 |
17 | // reducers
18 | import authReducer from './redux/reducers/authReducer';
19 | import filefolderReducer from './redux/reducers/filefolderReducer';
20 |
21 | const reducers = combineReducers({
22 | auth: authReducer,
23 | filefolders: filefolderReducer,
24 | });
25 |
26 | const store = createStore(
27 | reducers,
28 | composeWithDevTools(applyMiddleware(thunk))
29 | );
30 |
31 | const root = ReactDOM.createRoot(document.getElementById('root'));
32 |
33 | root.render(
34 |
35 |
36 |
37 |
38 |
39 | );
40 |
--------------------------------------------------------------------------------
/src/models/docs.js:
--------------------------------------------------------------------------------
1 | const docModel = (uid, name, path, parent) => {
2 | const model = {
3 | createdAt: new Date(),
4 | createdBy: uid,
5 | lastAccessed: new Date(),
6 | name: name,
7 | updatedAt: new Date(),
8 | path: path,
9 | parent: parent,
10 | };
11 |
12 | return model;
13 | };
14 |
15 | export default docModel;
16 |
--------------------------------------------------------------------------------
/src/models/files.js:
--------------------------------------------------------------------------------
1 | const fileModel = (uid, parent, data, name, url, path) => {
2 | const model = {
3 | createdAt: new Date(),
4 | createdBy: uid,
5 | data: data,
6 | name: name,
7 | parent: parent,
8 | updatedAt: new Date(),
9 | url: url,
10 | path: path,
11 | };
12 |
13 | return model;
14 | };
15 |
16 | export default fileModel;
17 |
--------------------------------------------------------------------------------
/src/models/users.js:
--------------------------------------------------------------------------------
1 | import { database } from "../API/firebase";
2 |
3 | const userModel = (email, name, uid) => {
4 | const model = {
5 | createdAt: database.date,
6 | docs: [],
7 | email: email,
8 | image: null,
9 | lastLogin: database.date,
10 | name: name,
11 | uid: uid,
12 | updatedAt: database.date,
13 | };
14 | return model;
15 | };
16 |
17 | export default userModel;
18 |
--------------------------------------------------------------------------------
/src/redux/actionCreators/authActionCreators.js:
--------------------------------------------------------------------------------
1 | import { toast } from "react-toastify";
2 | import { auth, database } from "../../API/firebase";
3 | import userModel from "../../models/users";
4 | import { RESET_USER, SET_USER } from "../actions/authActions";
5 | import { RESET_FOLDERS_FILES } from "../actions/filefoldersActions";
6 |
7 | const setUser = (data) => ({
8 | type: SET_USER,
9 | payload: data,
10 | });
11 |
12 | const resetUser = () => ({
13 | type: RESET_USER,
14 | });
15 |
16 | export const registerUser =
17 | ({ name, email, password }, setError) =>
18 | (dispatch) => {
19 | auth
20 | .createUserWithEmailAndPassword(email, password)
21 | .then((user) => {
22 | setError("");
23 | const newUser = userModel(email, name, user.user.uid);
24 | auth.currentUser.updateProfile({
25 | displayName: name,
26 | });
27 |
28 | database.users.add(newUser).then((usr) => {
29 | dispatch(
30 | setUser({
31 | userId: user.user.uid,
32 | user: { data: user.user.providerData[0] },
33 | })
34 | );
35 | toast.success("User registered successfully!!");
36 | });
37 | })
38 | .catch((err) => {
39 | console.log(err);
40 | if (err.code === "auth/email-already-in-use") {
41 | setError("Email Already Exists!");
42 | }
43 | });
44 | };
45 |
46 | export const loginUser =
47 | ({ email, password }, setError) =>
48 | (dispatch) => {
49 | auth
50 | .signInWithEmailAndPassword(email, password)
51 | .then(async (user) => {
52 | const usr = await database.users
53 | .where("uid", "==", user.user.uid)
54 | .get();
55 | console.log(usr.docs);
56 | })
57 | .catch(() => {
58 | setError("Invalid Email Or Password!");
59 | });
60 | };
61 |
62 | export const getUser = () => (dispatch) => {
63 | auth.onAuthStateChanged(function (user) {
64 | if (user) {
65 | dispatch(
66 | setUser({
67 | userId: auth.currentUser.uid,
68 | user: { data: auth.currentUser.providerData[0] },
69 | })
70 | );
71 | } else {
72 | dispatch(resetUser());
73 | }
74 | });
75 | };
76 |
77 | const reserFilesFolders = () => ({
78 | type: RESET_FOLDERS_FILES,
79 | });
80 |
81 | export const logoutUser = () => (dispatch) => {
82 | auth.signOut().then(() => {
83 | dispatch(resetUser());
84 | dispatch(reserFilesFolders());
85 | });
86 | };
87 |
--------------------------------------------------------------------------------
/src/redux/actionCreators/filefoldersActionCreators.js:
--------------------------------------------------------------------------------
1 | import { toast } from 'react-toastify';
2 | import { database, storage } from '../../API/firebase';
3 | import docModel from '../../models/docs';
4 | import fileModel from '../../models/files';
5 | import {
6 | SET_LOADING,
7 | SET_ADMIN_FILES,
8 | SET_ADMIN_FOLDERS,
9 | SET_USER_FOLDERS,
10 | ADD_USER_FOLDER,
11 | SET_USER_FILES,
12 | ADD_USER_FILE,
13 | UPDATE_USER_FILE_DATA,
14 | SELECT_ITEM,
15 | DESELECT_ITEM,
16 | DESELECT_ALL,
17 | SELECT_ITEMS,
18 | DESELECT_ITEMS,
19 | DELETE_FILES,
20 | DELETE_FOLDERS,
21 | } from '../actions/filefoldersActions';
22 |
23 | const setLoading = (data) => ({
24 | type: SET_LOADING,
25 | payload: data,
26 | });
27 | const setAdminFiles = (data) => ({
28 | type: SET_ADMIN_FILES,
29 | payload: data,
30 | });
31 | const setAdminFolders = (data) => ({
32 | type: SET_ADMIN_FOLDERS,
33 | payload: data,
34 | });
35 |
36 | export const getAdminFolders = () => (dispatch) => {
37 | dispatch(setLoading(true));
38 |
39 | database.docs
40 | .where('createdBy', '==', 'admin')
41 | .get()
42 | .then((folders) => {
43 | const allFolders = [];
44 | folders.docs.forEach((doc) => {
45 | allFolders.push({ data: doc.data(), docId: doc.id });
46 | });
47 | dispatch(setAdminFolders(allFolders));
48 | dispatch(setLoading(false));
49 | })
50 | .catch((err) => {
51 | toast.error('Failed to fetch data!');
52 | });
53 | };
54 | export const getAdminFiles = () => (dispatch) => {
55 | database.files
56 | .where('createdBy', '==', 'admin')
57 | .get()
58 | .then((files) => {
59 | const allFiles = [];
60 | files.docs.forEach((doc) => {
61 | allFiles.push({ data: doc.data(), docId: doc.id });
62 | });
63 | dispatch(setAdminFiles(allFiles));
64 | })
65 | .catch((err) => {
66 | toast.error('Failed to fetch data!');
67 | });
68 | };
69 |
70 | const setUserFolders = (data) => ({
71 | type: SET_USER_FOLDERS,
72 | payload: data,
73 | });
74 |
75 | export const getUserFolders = (userId) => async (dispatch) => {
76 | if (userId) {
77 | database.docs
78 | .where('createdBy', '==', userId)
79 | .get()
80 | .then((folders) => {
81 | const allFolders = [];
82 | folders.docs.forEach((doc) => {
83 | allFolders.push({ data: doc.data(), docId: doc.id });
84 | });
85 | dispatch(setUserFolders(allFolders));
86 | })
87 | .catch((err) => {
88 | console.log('foldererr', err);
89 | toast.error('Failed to fetch data!');
90 | });
91 | }
92 | };
93 |
94 | const addUserFolder = (data) => ({
95 | type: ADD_USER_FOLDER,
96 | payload: data,
97 | });
98 |
99 | export const addFolderUser = (name, userId, parent, path) => (dispatch) => {
100 | database.docs
101 | .add(docModel(userId, name, path, parent))
102 | .then(async (doc) => {
103 | const data = await doc.get();
104 | dispatch(addUserFolder({ data: data.data(), docId: data.id }));
105 | toast.success('Folder added Successfully!');
106 | })
107 | .catch((err) => {
108 | console.log(err);
109 | toast.error('Something went wrong!');
110 | });
111 | };
112 |
113 | const setUserFiles = (data) => ({
114 | type: SET_USER_FILES,
115 | payload: data,
116 | });
117 |
118 | export const getUserFiles = (userId) => (dispatch) => {
119 | if (userId) {
120 | database.files
121 | .where('createdBy', '==', userId)
122 | .get()
123 | .then((files) => {
124 | const allFiles = [];
125 | files.docs.forEach((doc) => {
126 | allFiles.push({ data: doc.data(), docId: doc.id });
127 | });
128 | dispatch(setUserFiles(allFiles));
129 | })
130 | .catch((err) => {
131 | console.log('foldererr', err);
132 | toast.error('Failed to fetch data!');
133 | });
134 | }
135 | };
136 |
137 | const addUserFile = (data) => ({
138 | type: ADD_USER_FILE,
139 | payload: data,
140 | });
141 |
142 | export const addFileUser =
143 | ({ uid, parent, data, name, url, path }) =>
144 | (dispatch) => {
145 | database.files
146 | .add(fileModel(uid, parent, data, name, url, path))
147 | .then(async (doc) => {
148 | const data = await doc.get();
149 | dispatch(addUserFile({ data: data.data(), docId: data.id }));
150 | if (data.data().url === '') {
151 | toast.success('File created Successfully!');
152 | toast.success('You can double click on the file to open the editor!');
153 | } else {
154 | toast.success('File uploaded Successfully!');
155 | }
156 | })
157 | .catch((err) => {
158 | console.log(err);
159 | toast.error('Something went wrong!');
160 | });
161 | };
162 |
163 | const updateUserFileData = (data) => ({
164 | type: UPDATE_USER_FILE_DATA,
165 | payload: data,
166 | });
167 |
168 | export const userFileDataUpdate = (data, docId) => (dispatch) => {
169 | database.files
170 | .doc(docId)
171 | .update({
172 | updatedAt: new Date(),
173 | data: data,
174 | })
175 | .then(() => {
176 | dispatch(updateUserFileData({ data, docId }));
177 | toast.success('Saved Successfully!!');
178 |
179 | document.querySelector('.CodeMirror').focus();
180 | })
181 | .catch((err) => {
182 | console.log(err);
183 | toast.error('Something went wrong!');
184 | });
185 | };
186 |
187 | export const selectItem = (data) => ({
188 | type: SELECT_ITEM,
189 | payload: data,
190 | });
191 |
192 | export const deselectItem = (data) => ({
193 | type: DESELECT_ITEM,
194 | payload: data,
195 | });
196 |
197 | export const deselectAll = () => ({
198 | type: DESELECT_ALL,
199 | });
200 |
201 | const selectItems = (data) => ({
202 | type: SELECT_ITEMS,
203 | payload: data,
204 | });
205 |
206 | const deselectItems = (data) => ({
207 | type: DESELECT_ITEMS,
208 | payload: data,
209 | });
210 |
211 | const deleteFilesAction = (doctIds) => ({
212 | type: DELETE_FILES,
213 | payload: doctIds,
214 | });
215 |
216 | export const deleteFoldersAction = (doctIds) => ({
217 | type: DELETE_FOLDERS,
218 | payload: doctIds,
219 | });
220 |
221 | export const getSubItems = (state, data = {}) => {
222 | const {
223 | filefolders: { userFolders, userFiles },
224 | } = state();
225 |
226 | const folderFiles = userFiles
227 | .filter((file) => file.data.parent === data.docId)
228 | .map((file) => ({ ...file, type: 'file' }));
229 |
230 | const subFolders = userFolders
231 | .filter(
232 | (folder) =>
233 | folder.data.path.find((path) => path.id === data.docId) !== undefined
234 | )
235 | .map((folder) => ({ ...folder, type: 'folder' }));
236 |
237 | return {
238 | folderFiles,
239 | subFolders,
240 | };
241 | };
242 |
243 | export const selectFolder = (data) => (dispatch, state) => {
244 | const { folderFiles, subFolders } = getSubItems(state, data);
245 |
246 | dispatch(
247 | selectItems([{ ...data, type: 'folder' }, ...folderFiles, ...subFolders])
248 | );
249 | };
250 |
251 | export const deselctFolder = (docId) => (dispatch, state) => {
252 | const { folderFiles, subFolders } = getSubItems(state, { docId });
253 |
254 | const filesDocIds = folderFiles.map((file) => file.docId);
255 | const foldersDocIds = subFolders.map((folder) => folder.docId);
256 |
257 | dispatch(deselectItems([docId, ...filesDocIds, ...foldersDocIds]));
258 | };
259 |
260 | const deleteFiles = (files) => {
261 | const promises = files.map((file) => {
262 | if (file.data.url) {
263 | try {
264 | database.files.doc(file.docId).delete();
265 | storage.refFromURL(file.data.url).delete();
266 | } catch (err) {
267 | console.log(err);
268 | console.log('Failed to delete file', file.data.name);
269 | }
270 |
271 | return true;
272 | }
273 | return database.files.doc(file.docId).delete();
274 | });
275 |
276 | return Promise.all(promises);
277 | };
278 |
279 | export const deleteItems = () => (dispatch, state) => {
280 | const {
281 | filefolders: { selectedItems },
282 | } = state();
283 |
284 | const files = selectedItems.filter((item) => item.type === 'file');
285 | const folders = selectedItems.filter((item) => item.type === 'folder');
286 |
287 | deleteFiles(files).then((response) => {
288 | if (response.length === files.length) {
289 | dispatch(deleteFilesAction(files.map((file) => file.docId)));
290 | }
291 |
292 | folders.forEach((folder) => {
293 | database.docs.doc(folder.docId).delete();
294 | });
295 |
296 | dispatch(deleteFoldersAction(folders.map((folder) => folder.docId)));
297 | dispatch(deselectAll());
298 |
299 | toast.success('Deleted Items Successfully!');
300 | });
301 | };
302 |
--------------------------------------------------------------------------------
/src/redux/actions/authActions.js:
--------------------------------------------------------------------------------
1 | export const SET_USER = "SET_USER";
2 | export const RESET_USER = "RESET_USER";
3 |
--------------------------------------------------------------------------------
/src/redux/actions/filefoldersActions.js:
--------------------------------------------------------------------------------
1 | export const SET_LOADING = 'SET_LOADING';
2 | export const SET_ADMIN_FILES = 'SET_ADMIN_FILES';
3 | export const SET_ADMIN_FOLDERS = 'SET_ADMIN_FOLDERS';
4 | export const SET_USER_FOLDERS = 'SET_USER_FOLDERS';
5 | export const ADD_USER_FOLDER = 'ADD_USER_FOLDER';
6 | export const RESET_FOLDERS_FILES = 'RESET_FOLDERS_FILES';
7 | export const SET_USER_FILES = 'SET_USER_FILES';
8 | export const ADD_USER_FILE = 'ADD_USER_FILE';
9 | export const UPDATE_USER_FILE_DATA = 'UPDATE_USER_FILE_DATA';
10 | export const SELECT_ITEM = 'SELECT_ITEM';
11 | export const SELECT_ITEMS = 'SELECT_ITEMS';
12 | export const DESELECT_ITEM = 'DESELECT_ITEM';
13 | export const DESELECT_ITEMS = 'DESELECT_ITEMS';
14 | export const DESELECT_ALL = 'DESELECT_ALL';
15 | export const DELETE_FILES = 'DELETE_FILES';
16 | export const DELETE_FOLDERS = 'DELETE_FOLDERS';
17 |
--------------------------------------------------------------------------------
/src/redux/reducers/authReducer.js:
--------------------------------------------------------------------------------
1 | import { RESET_USER, SET_USER } from "../actions/authActions";
2 |
3 | const initialState = {
4 | isLoggedIn: false,
5 | user: null,
6 | userId: null,
7 | };
8 |
9 | const authReducer = (state = initialState, { type, payload }) => {
10 | switch (type) {
11 | case SET_USER:
12 | state = {
13 | isLoggedIn: true,
14 | user: payload.user,
15 | userId: payload.userId,
16 | };
17 | return state;
18 | case RESET_USER:
19 | state = initialState;
20 | return state;
21 | default:
22 | return state;
23 | }
24 | };
25 | export default authReducer;
26 |
--------------------------------------------------------------------------------
/src/redux/reducers/filefolderReducer.js:
--------------------------------------------------------------------------------
1 | import {
2 | ADD_USER_FILE,
3 | ADD_USER_FOLDER,
4 | RESET_FOLDERS_FILES,
5 | SELECT_ITEM,
6 | SELECT_ITEMS,
7 | DESELECT_ITEM,
8 | DESELECT_ITEMS,
9 | DESELECT_ALL,
10 | SET_ADMIN_FILES,
11 | SET_ADMIN_FOLDERS,
12 | SET_LOADING,
13 | SET_USER_FILES,
14 | SET_USER_FOLDERS,
15 | UPDATE_USER_FILE_DATA,
16 | DELETE_FILES,
17 | DELETE_FOLDERS,
18 | } from '../actions/filefoldersActions';
19 |
20 | const initialState = {
21 | isLoading: true,
22 | folder: 'root',
23 | userFolders: null,
24 | userFiles: null,
25 | adminFolders: null,
26 | adminFiles: null,
27 | selectedItems: [],
28 | };
29 |
30 | const filefolderReducer = (state = initialState, { type, payload }) => {
31 | switch (type) {
32 | case SET_LOADING:
33 | state = { ...state, isLoading: payload };
34 | return state;
35 | case SET_ADMIN_FILES:
36 | state = { ...state, adminFiles: payload };
37 | return state;
38 | case SET_ADMIN_FOLDERS:
39 | state = { ...state, adminFolders: payload };
40 | return state;
41 | case SET_USER_FOLDERS:
42 | state = { ...state, userFolders: payload };
43 | return state;
44 | case ADD_USER_FOLDER:
45 | state = { ...state, userFolders: [...state.userFolders, payload] };
46 | return state;
47 | case RESET_FOLDERS_FILES:
48 | state = initialState;
49 | return state;
50 | case SET_USER_FILES:
51 | state = { ...state, userFiles: payload };
52 | return state;
53 | case ADD_USER_FILE:
54 | state = { ...state, userFiles: [...state.userFiles, payload] };
55 | return state;
56 | case UPDATE_USER_FILE_DATA:
57 | const currentUserFile = state.userFiles.find(
58 | (file) => file.docId === payload.docId
59 | );
60 | currentUserFile.data.data = payload.data;
61 | state = {
62 | ...state,
63 | userFiles: state.userFiles.map((file) =>
64 | file.docId === payload.docId ? currentUserFile : file
65 | ),
66 | };
67 | return state;
68 | case SELECT_ITEM:
69 | state = { ...state, selectedItems: [...state.selectedItems, payload] };
70 | return state;
71 | case SELECT_ITEMS:
72 | state = { ...state, selectedItems: [...state.selectedItems, ...payload] };
73 | return state;
74 | case DESELECT_ITEM:
75 | state = {
76 | ...state,
77 | selectedItems: state.selectedItems.filter(
78 | (item) => item.docId !== payload.docId
79 | ),
80 | };
81 | return state;
82 | case DESELECT_ITEMS:
83 | state = {
84 | ...state,
85 | selectedItems: state.selectedItems.filter(
86 | (item) => !payload.includes(item.docId)
87 | ),
88 | };
89 | return state;
90 | case DESELECT_ALL:
91 | state = { ...state, selectedItems: [] };
92 | return state;
93 | case DELETE_FILES:
94 | state = {
95 | ...state,
96 | userFiles: state.userFiles.filter(
97 | (file) => !payload.includes(file.docId)
98 | ),
99 | };
100 | return state;
101 | case DELETE_FOLDERS:
102 | state = {
103 | ...state,
104 | userFolders: state.userFolders.filter(
105 | (folder) => !payload.includes(folder.docId)
106 | ),
107 | };
108 | return state;
109 | default:
110 | return state;
111 | }
112 | };
113 | export default filefolderReducer;
114 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react-swc';
3 |
4 | export default defineConfig({
5 | base: '/',
6 | plugins: [react()],
7 | });
8 |
--------------------------------------------------------------------------------