From 9ef2003b7f4b3e0ca88998b7a4e5c382f2582c65 Mon Sep 17 00:00:00 2001 From: sabreen-parveen Date: Fri, 8 Apr 2022 02:52:02 +0530 Subject: [PATCH 1/5] Add dashboard --- .gitignore | 4 +- .../Hooks/useFileGetRequestWithoutAuth.js | 41 +++++++ components/Hooks/useGetRequest.js | 43 ++++++++ components/Hooks/useGetRequestWithoutAuth.js | 38 +++++++ components/Hooks/useIsMounted.js | 13 +++ .../Hooks/useLazyGetRequestWithoutAuth.js | 40 +++++++ components/Hooks/useLazyPatchRequest.js | 50 +++++++++ components/Hooks/useLazyPostRequest.js | 50 +++++++++ components/Hooks/useSignup.js | 46 ++++++++ components/UI/Dashboard/Dashboard.js | 3 + components/UI/Dashboard/DashboardSelection.js | 3 + components/UI/Dashboard/DatePickerWrapper.js | 34 ++++++ components/UI/Dashboard/Section.js | 7 ++ components/UI/Dashboard/Sidebar.js | 3 + components/UI/Files/DisplayFileData.js | 45 ++++++++ components/UI/Files/DisplaySavedFileData.js | 34 ++++++ components/UI/Files/FileHandler.js | 83 ++++++++++++++ components/UI/Files/FileSelector.js | 23 ++++ components/UI/Files/ShowFiles.js | 55 ++++++++++ components/UI/Files/UploaderFunctions.js | 7 ++ components/UI/Files/utils.js | 102 ++++++++++++++++++ components/UI/Footer.js | 4 +- components/UI/Nav.js | 6 +- components/UI/Spinner.js | 13 ++- components/UI/buttons.js | 29 +++++ components/contexts/AuthContextProvider.js | 4 +- components/utils/error.js | 11 ++ components/utils/requests.js | 44 ++++++++ lib/Dashboards/OnlineCompiler.js | 12 +++ lib/Dashboards/PageNotExist.js | 12 +++ lib/Dashboards/file/ManageFile.js | 36 +++++++ lib/Dashboards/file/UploadFile.js | 95 ++++++++++++++++ lib/Dashboards/file/ViewFile.js | 12 +++ lib/Dashboards/profile/EditProfile.js | 12 +++ lib/Dashboards/profile/ProfileSelection.js | 55 ++++++++++ lib/Dashboards/profile/ViewProfile.js | 35 ++++++ lib/Hooks/useSignup.js | 4 + package.json | 1 + pages/confirm-message.js | 2 +- pages/dashboard.js | 17 +++ pages/dashboard/[type].js | 81 ++++++++++++++ pages/index.js | 19 ++-- pages/welcome.js | 2 +- src/config.js | 8 ++ styles/globals.css | 2 +- tailwind.config.js | 3 + 46 files changed, 1222 insertions(+), 21 deletions(-) create mode 100644 components/Hooks/useFileGetRequestWithoutAuth.js create mode 100644 components/Hooks/useGetRequest.js create mode 100644 components/Hooks/useGetRequestWithoutAuth.js create mode 100644 components/Hooks/useIsMounted.js create mode 100644 components/Hooks/useLazyGetRequestWithoutAuth.js create mode 100644 components/Hooks/useLazyPatchRequest.js create mode 100644 components/Hooks/useLazyPostRequest.js create mode 100644 components/Hooks/useSignup.js create mode 100644 components/UI/Dashboard/Dashboard.js create mode 100644 components/UI/Dashboard/DashboardSelection.js create mode 100644 components/UI/Dashboard/DatePickerWrapper.js create mode 100644 components/UI/Dashboard/Section.js create mode 100644 components/UI/Dashboard/Sidebar.js create mode 100644 components/UI/Files/DisplayFileData.js create mode 100644 components/UI/Files/DisplaySavedFileData.js create mode 100644 components/UI/Files/FileHandler.js create mode 100644 components/UI/Files/FileSelector.js create mode 100644 components/UI/Files/ShowFiles.js create mode 100644 components/UI/Files/UploaderFunctions.js create mode 100644 components/UI/Files/utils.js create mode 100644 components/utils/error.js create mode 100644 components/utils/requests.js create mode 100644 lib/Dashboards/OnlineCompiler.js create mode 100644 lib/Dashboards/PageNotExist.js create mode 100644 lib/Dashboards/file/ManageFile.js create mode 100644 lib/Dashboards/file/UploadFile.js create mode 100644 lib/Dashboards/file/ViewFile.js create mode 100644 lib/Dashboards/profile/EditProfile.js create mode 100644 lib/Dashboards/profile/ProfileSelection.js create mode 100644 lib/Dashboards/profile/ViewProfile.js create mode 100644 pages/dashboard.js create mode 100644 pages/dashboard/[type].js diff --git a/.gitignore b/.gitignore index 685bab1..b1b8cd2 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,6 @@ yarn-error.log* # vercel .vercel -package-lock.json \ No newline at end of file +package-lock.json + +.env \ No newline at end of file diff --git a/components/Hooks/useFileGetRequestWithoutAuth.js b/components/Hooks/useFileGetRequestWithoutAuth.js new file mode 100644 index 0000000..3ff15bf --- /dev/null +++ b/components/Hooks/useFileGetRequestWithoutAuth.js @@ -0,0 +1,41 @@ +import { useEffect, useState } from "react"; +import { API } from "aws-amplify"; + +import { + handleBackendError, + isBackendError, +} from "../../components/utils/error"; +import useIsMounted from "./useIsMounted"; + +export default function useFileGetRequestWithoutAuth(route, options = {}) { + const isMounted = useIsMounted(); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(true); + const [data, setData] = useState(options.initialDataState || null); + + useEffect(() => { + async function makeRequest() { + try { + const response = await API.get("Diogenes", route); + if (isMounted.current) { + setLoading(false); + setData(response); + } + } catch (err) { + let error = err; + if (isBackendError(err)) { + error = handleBackendError(err); + } + console.error(error); + + if (isMounted.current) { + setError(error); + setLoading(false); + } + } + } + makeRequest(); + }, [route, isMounted]); + + return { data, error, loading }; +} diff --git a/components/Hooks/useGetRequest.js b/components/Hooks/useGetRequest.js new file mode 100644 index 0000000..68fe2ff --- /dev/null +++ b/components/Hooks/useGetRequest.js @@ -0,0 +1,43 @@ +import { useEffect, useState, useCallback } from "react"; + +import { + handleBackendError, + isBackendError, +} from "../../components/utils/error"; +import { getRequestWithAuth } from "../../components/utils/requests"; +import useIsMounted from "./useIsMounted"; + +export default function useGetRequest(route, options = {}) { + const isMounted = useIsMounted(); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(true); + const [data, setData] = useState(options.initialDataState || null); + + useEffect(() => { + async function makeRequest() { + setLoading(true); + try { + const response = await getRequestWithAuth(route); + console.log(response); + if (isMounted.current) { + setLoading(false); + setData(response); + } + } catch (err) { + let error = err; + if (isBackendError(err)) { + error = handleBackendError(err); + } + console.error(error); + + if (isMounted.current) { + setError(error); + setLoading(false); + } + } + } + makeRequest(); + }, [route, isMounted]); + + return { data, error, loading }; +} diff --git a/components/Hooks/useGetRequestWithoutAuth.js b/components/Hooks/useGetRequestWithoutAuth.js new file mode 100644 index 0000000..962b999 --- /dev/null +++ b/components/Hooks/useGetRequestWithoutAuth.js @@ -0,0 +1,38 @@ +import { useEffect, useState } from "react"; +import { API } from "aws-amplify"; + +import { handleBackendError, isBackendError } from "components/utils/error"; +import useIsMounted from "./useIsMounted"; + +export default function useGetRequestWithoutAuth(route, options = {}) { + const isMounted = useIsMounted(); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(true); + const [data, setData] = useState(options.initialDataState || null); + + useEffect(() => { + async function makeRequest() { + try { + const response = await API.get("sourceQwik", route); + if (isMounted.current) { + setLoading(false); + setData(response); + } + } catch (err) { + let error = err; + if (isBackendError(err)) { + error = handleBackendError(err); + } + console.error(error); + + if (isMounted.current) { + setError(error); + setLoading(false); + } + } + } + makeRequest(); + }, [route, isMounted]); + + return { data, error, loading }; +} diff --git a/components/Hooks/useIsMounted.js b/components/Hooks/useIsMounted.js new file mode 100644 index 0000000..564bf0b --- /dev/null +++ b/components/Hooks/useIsMounted.js @@ -0,0 +1,13 @@ +import { useRef, useEffect } from "react"; + +export default function useIsMounted() { + const isMounted = useRef(true); + + useEffect(() => { + return () => { + isMounted.current = false; + }; + }, []); + + return isMounted; +} diff --git a/components/Hooks/useLazyGetRequestWithoutAuth.js b/components/Hooks/useLazyGetRequestWithoutAuth.js new file mode 100644 index 0000000..574d168 --- /dev/null +++ b/components/Hooks/useLazyGetRequestWithoutAuth.js @@ -0,0 +1,40 @@ +import { useCallback, useState } from "react"; +import { API } from "aws-amplify"; + +import { handleBackendError, isBackendError } from "components/utils/error"; + +export default function useLazyGetRequestWithoutAuth(options = {}) { + const [data, setData] = useState(null); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); + + const makeGetRequest = useCallback( + async (route) => { + if (error) { + setError(null); + } + if (data) { + setData(null); + } + setLoading(true); + try { + const response = await API.get("sourceQwik", route); + setLoading(false); + setData(response); + if (options.onSuccess) { + options.onSuccess(); + } + } catch (err) { + let error = err; + if (isBackendError(err)) { + error = handleBackendError(err); + } + setError(error); + setLoading(false); + } + }, + [error, options, data] + ); + + return { data, error, loading, makeGetRequest }; +} diff --git a/components/Hooks/useLazyPatchRequest.js b/components/Hooks/useLazyPatchRequest.js new file mode 100644 index 0000000..4a7e8c8 --- /dev/null +++ b/components/Hooks/useLazyPatchRequest.js @@ -0,0 +1,50 @@ +import { useCallback, useState } from "react"; + +import { + handleBackendError, + isBackendError, +} from "../../components/utils/error"; +import { patchRequestWithAuth } from "../../components/utils/requests"; +import useIsMounted from "./useIsMounted"; + +export default function useLazyPatchRequest(route, options) { + const isMounted = useIsMounted(); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); + // console.log(options); + const makePatchRequest = useCallback( + (body) => + (async (body) => { + // console.log(body); + // console.log(error); + if (error) { + setError(null); + makePatchRequest; + } + setLoading(true); + try { + const response = await patchRequestWithAuth(route, { body }); + console.log(response); + if (isMounted.current) { + setLoading(false); + if (options.onSuccess) { + options.onSuccess(response, body); + } + } + } catch (err) { + let error = err; + console.log(error); + if (isBackendError(err)) { + error = handleBackendError(err); + } + if (isMounted.current) { + setError(error); + setLoading(false); + } + } + })(body), + [error, route, options, isMounted] + ); + + return { error, loading, makePatchRequest }; +} diff --git a/components/Hooks/useLazyPostRequest.js b/components/Hooks/useLazyPostRequest.js new file mode 100644 index 0000000..03e4eec --- /dev/null +++ b/components/Hooks/useLazyPostRequest.js @@ -0,0 +1,50 @@ +import { useCallback, useState } from "react"; + +import { postRequestWithAuth } from "../../components/utils/requests"; +import { + handleBackendError, + isBackendError, +} from "../../components/utils/error"; +import useIsMounted from "./useIsMounted"; + +export default function useLazyPostRequest(route, options = {}) { + const isMounted = useIsMounted(); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); + const [data, setData] = useState(null); + + const makePostRequest = useCallback( + (body) => + (async function (body) { + if (error) { + setError(null); + } + setLoading(true); + + try { + const response = await postRequestWithAuth(route, { body }); + // console.log(response); + if (isMounted.current) { + setLoading(false); + setData(response); + if (options.onSuccess) { + options.onSuccess(response); + } + } + } catch (err) { + let error = err; + if (isBackendError(err)) { + error = handleBackendError(err); + } + console.error(error); + if (isMounted.current) { + setError(error); + setLoading(false); + } + } + })(body), + [route, error, options, isMounted] + ); + + return { data, error, loading, makePostRequest }; +} diff --git a/components/Hooks/useSignup.js b/components/Hooks/useSignup.js new file mode 100644 index 0000000..f3e1154 --- /dev/null +++ b/components/Hooks/useSignup.js @@ -0,0 +1,46 @@ +import { useState, useCallback } from "react"; + +import signUp from "components/Auth/Signup"; +import useIsMounted from "./useIsMounted"; + +export default function useSignup(options) { + const isMounted = useIsMounted(); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); + + const SignupUser = useCallback( + (data) => + (async function (data) { + if (error) { + setError(null); + } + setLoading(true); + + try { + const response = await signUp( + data.email, + data.email, + data.password, + data.phone, + data.firstName, + data.middleName, + data.lastName + ); + console.log(response); + if (isMounted.current) { + setLoading(false); + options.onSuccess(); + } + } catch (err) { + console.error(err); + if (isMounted.current) { + setError(err); + setLoading(false); + } + } + })(data), + [options, error, isMounted] + ); + + return { error, loading, SignupUser }; +} diff --git a/components/UI/Dashboard/Dashboard.js b/components/UI/Dashboard/Dashboard.js new file mode 100644 index 0000000..09afae1 --- /dev/null +++ b/components/UI/Dashboard/Dashboard.js @@ -0,0 +1,3 @@ +export default function Dashboard({ children }) { + return
{children}
; +} diff --git a/components/UI/Dashboard/DashboardSelection.js b/components/UI/Dashboard/DashboardSelection.js new file mode 100644 index 0000000..73b38a2 --- /dev/null +++ b/components/UI/Dashboard/DashboardSelection.js @@ -0,0 +1,3 @@ +export default function DashboardSelection({ children }) { + return
{children}
; +} diff --git a/components/UI/Dashboard/DatePickerWrapper.js b/components/UI/Dashboard/DatePickerWrapper.js new file mode 100644 index 0000000..ad3c491 --- /dev/null +++ b/components/UI/Dashboard/DatePickerWrapper.js @@ -0,0 +1,34 @@ +import DatePicker, { CalendarContainer } from "react-datepicker"; +import { useField, useFormikContext } from "formik"; + +export default function DatePickerWrapper({ ...props }) { + const { setFieldValue, setFieldTouched } = useFormikContext(); + const [field] = useField(props); + + const calendarContainer = ({ className, children }) => { + return ( +
+ +
{children}
+
+
+ ); + }; + + return ( + { + setFieldValue(field.name, val); + }} + minDate={new Date()} + onChangeRaw={(e) => { + setFieldTouched(field.name, true, true); + }} + dateFormat="dd/MM/yyyy" + calendarContainer={calendarContainer} + /> + ); +} diff --git a/components/UI/Dashboard/Section.js b/components/UI/Dashboard/Section.js new file mode 100644 index 0000000..9ab30fb --- /dev/null +++ b/components/UI/Dashboard/Section.js @@ -0,0 +1,7 @@ +export function ButtonsContainer({ children }) { + return
{children}
; +} + +export function Section({ children }) { + return
{children}
; +} diff --git a/components/UI/Dashboard/Sidebar.js b/components/UI/Dashboard/Sidebar.js new file mode 100644 index 0000000..7ab8c6a --- /dev/null +++ b/components/UI/Dashboard/Sidebar.js @@ -0,0 +1,3 @@ +export default function Sidebar({ children }) { + return
{children}
; +} diff --git a/components/UI/Files/DisplayFileData.js b/components/UI/Files/DisplayFileData.js new file mode 100644 index 0000000..63a9ed5 --- /dev/null +++ b/components/UI/Files/DisplayFileData.js @@ -0,0 +1,45 @@ +import fileSize from "filesize"; +import { TiDeleteOutline } from "react-icons/ti"; + +import { getFileType, trimFilename, GetFileIcon } from "./utils"; + +export default function DisplayFileData({ + index, + file, + showLargeFileError, + removeFileFn, +}) { + return ( +
+
+ +
+

{trimFilename(file.name)}

+
+ {getFileType(file.type).toUpperCase()} {fileSize(file.size)} + {showLargeFileError ? ( + + {" "} + Maximum file size limit is 10MB + + ) : null} +
+
+ + +
+
+ ); +} diff --git a/components/UI/Files/DisplaySavedFileData.js b/components/UI/Files/DisplaySavedFileData.js new file mode 100644 index 0000000..7c9a3b9 --- /dev/null +++ b/components/UI/Files/DisplaySavedFileData.js @@ -0,0 +1,34 @@ +import fileSize from "filesize"; +import { RiDeleteBin7Line } from "react-icons/ri"; + +import { + GetFileIcon, + getFileNameFromPath, + getFileType, + trimFilename, +} from "./utils"; + +export default function DisplaySavedFileData({ file, deleteFileFn }) { + return ( +
+
+ +
+ + {trimFilename(getFileNameFromPath(file.data.path))} + +
+ {getFileType(file.data.mimeType).toUpperCase()}{" "} + {fileSize(file.data.fileSize)} +
+
+ +
+
+ ); +} diff --git a/components/UI/Files/FileHandler.js b/components/UI/Files/FileHandler.js new file mode 100644 index 0000000..3f86be2 --- /dev/null +++ b/components/UI/Files/FileHandler.js @@ -0,0 +1,83 @@ +import { FiInbox } from "react-icons/fi"; + +import FileSelector from "./FileSelector"; +import DisplayFileData from "./DisplayFileData"; + +export default function FileHandler({ + files, + setFiles, + setDisableFileUpload, + fileSizeLimit = 10000000, + customError, +}) { + function setFilesState(files) { + let disabled = false; + for (const file of files) { + if (isFileTooLarge(file) && disabled !== true) { + disabled = true; + } + } + setDisableFileUpload(disabled); + setFiles(files); + } + + function insertFiles(e) { + const selectedFiles = e.target.files; + const files = []; + for (const file of selectedFiles) { + files.push(file); + } + setFilesState(files); + } + + function removeFile(index) { + const allFiles = [...files]; + allFiles.splice(index, 1); + setFilesState(allFiles); + } + + function isFileTooLarge(file) { + if (file.size > fileSizeLimit) { + return true; + } + return false; + } + + return ( + <> + {files.length !== 0 ? ( +
+ {files.map((file, index) => ( + removeFile(index)} + /> + ))} +
+ ) : ( + <> +
+ +

Upload your files here

+

You can upload pdf, jpeg, png, ppt & doc files

+
+
+ {customError?.message} +
+ + )} + + + ); +} diff --git a/components/UI/Files/FileSelector.js b/components/UI/Files/FileSelector.js new file mode 100644 index 0000000..31dd62c --- /dev/null +++ b/components/UI/Files/FileSelector.js @@ -0,0 +1,23 @@ +import { acceptType } from "./utils"; + +export default function FileSelector({ + labelText = "Browse", + multiple = false, + insertFileFn, +}) { + return ( +
+ + +
+ ); +} diff --git a/components/UI/Files/ShowFiles.js b/components/UI/Files/ShowFiles.js new file mode 100644 index 0000000..c32a44b --- /dev/null +++ b/components/UI/Files/ShowFiles.js @@ -0,0 +1,55 @@ +import Spinner from "../Spinner"; +import { ShowError } from "../errors"; +import useFileGetRequestWithoutAuth from "../../../components/Hooks/useFileGetRequestWithoutAuth"; + +export default function ShowFiles({ fileIds, additionalData }) { + return ( + <> + {fileIds.map((fileId, index) => ( + + ))} + + ); +} + +function ShowFile({ fileId, additionalData }) { + const { data, error, loading } = useFileGetRequestWithoutAuth( + `/file/${fileId}` + ); + + return ( +
+ + + {data !== null ? ( + + ) : null} +
+ ); +} + +function ShowFileData({ data, showAdditionalData = false }) { + return ( +
+

+ Name: {data.data.path.split("/")[1]} +

+ {showAdditionalData ? ( +

+ S3 path: {data.data.path} +

+ ) : null} + + Open file + +
+ ); +} diff --git a/components/UI/Files/UploaderFunctions.js b/components/UI/Files/UploaderFunctions.js new file mode 100644 index 0000000..a9ab4fd --- /dev/null +++ b/components/UI/Files/UploaderFunctions.js @@ -0,0 +1,7 @@ +export function makeForm(formData) { + const form = new FormData(); + form.append("subId", formData.subId); + form.append("files", formData.file); + form.append("clientName", formData.clientName); + return form; +} diff --git a/components/UI/Files/utils.js b/components/UI/Files/utils.js new file mode 100644 index 0000000..c230d3b --- /dev/null +++ b/components/UI/Files/utils.js @@ -0,0 +1,102 @@ +import { BiFileBlank } from "react-icons/bi"; +import { + GrDocumentPdf, + GrDocumentText, + GrDocumentWord, + GrDocumentExcel, + GrDocumentPpt, +} from "react-icons/gr"; +import { FaRegFileImage } from "react-icons/fa"; + +// taken from: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types +const getIconAndTypeFromMimeType = { + "application/pdf": { + icon: , + type: "pdf", + }, + "image/png": { + icon: , + type: "png", + }, + "image/gif": { + icon: , + type: "gif", + }, + "image/jpeg": { + icon: , + type: "jpeg", + }, + "image/bmp": { + icon: , + type: "bmp", + }, + "text/csv": { + icon: , + type: "csv", + }, + "application/msword": { + icon: , + type: "word", + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document": { + icon: , + type: "word", + }, + "application/vnd.oasis.opendocument.presentation": { + icon: , + type: "ppt", + }, + "application/vnd.oasis.opendocument.spreadsheet": { + icon: , + type: "excel", + }, + "application/vnd.ms-powerpoint": { + icon: , + type: "ppt", + }, + "application/vnd.openxmlformats-officedocument.presentationml.presentation": { + icon: , + type: "ppt", + }, + "application/vnd.ms-excel": { + icon: , + type: "excel", + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { + icon: , + type: "excel", + }, +}; + +export const acceptType = Object.keys(getIconAndTypeFromMimeType).join(", "); + +export function GetFileIcon({ mimeType }) { + const fileTypeData = getIconAndTypeFromMimeType[mimeType]; + if (fileTypeData) { + return fileTypeData.icon; + } + return ; // default icon +} + +export function getFileType(mimeType) { + const fileTypeData = getIconAndTypeFromMimeType[mimeType]; + if (fileTypeData) { + return fileTypeData.type; + } + return "unknown"; +} + +export function trimFilename(name) { + if (name.length > 20) { + return name.slice(0, 20) + "..."; + } + return name; +} + +export function getFileNameFromPath(name) { + if (name.includes("/")) { + const [prefix, ...filename] = name.split("/"); + return filename.join("/"); + } + return name; +} diff --git a/components/UI/Footer.js b/components/UI/Footer.js index 52bef03..d44f01a 100644 --- a/components/UI/Footer.js +++ b/components/UI/Footer.js @@ -6,12 +6,12 @@ export default function Footer() {
- CoderSpace logo + /> */}

CoderSpace © 2021

diff --git a/components/UI/Nav.js b/components/UI/Nav.js index fb48276..e4de432 100644 --- a/components/UI/Nav.js +++ b/components/UI/Nav.js @@ -48,12 +48,12 @@ export default function Nav() { function Logo() { return (
- CoderSpace logo + /> */}
); } @@ -80,7 +80,7 @@ function NavLinks({ ctx, route, signoutUser }) {
  • -
  • diff --git a/components/UI/Spinner.js b/components/UI/Spinner.js index ecc07a8..dbdee60 100644 --- a/components/UI/Spinner.js +++ b/components/UI/Spinner.js @@ -1,4 +1,4 @@ -import Loader from "react-loader-spinner"; +import { Oval } from "react-loader-spinner"; export default function Spinner({ loading, message }) { return ( @@ -6,7 +6,16 @@ export default function Spinner({ loading, message }) { {loading ? (
    - + {/* */} +

    {message}

    diff --git a/components/UI/buttons.js b/components/UI/buttons.js index c47bad0..a1db27a 100644 --- a/components/UI/buttons.js +++ b/components/UI/buttons.js @@ -1,3 +1,5 @@ +import router from "next/router"; + export function FormSubmitBtn({ children, type, disabled }) { return ( ); } + +export function DashboardSidebarButton({ children, linkTo, highlight }) { + return ( + + ); +} + +export function DashboardSelectionButton({ children, linkTo, highlight }) { + return ( + + ); +} diff --git a/components/contexts/AuthContextProvider.js b/components/contexts/AuthContextProvider.js index d4e7aa0..1c97de4 100644 --- a/components/contexts/AuthContextProvider.js +++ b/components/contexts/AuthContextProvider.js @@ -5,7 +5,6 @@ import AuthContext from "./AuthContext"; export default function AuthContextProvider({ children }) { const [userDetail, setUserDetail] = useState(null); - const [identity, setIdentity] = useState(""); const [isLoggedIn, setIsLoggedIn] = useState(false); function changeUserDetail(details) { @@ -22,9 +21,8 @@ export default function AuthContextProvider({ children }) { changeUserDetail, isLoggedIn, changeLoginState, - identity, }), - [userDetail, isLoggedIn, identity] + [userDetail, isLoggedIn] ); useEffect(() => { diff --git a/components/utils/error.js b/components/utils/error.js new file mode 100644 index 0000000..4497116 --- /dev/null +++ b/components/utils/error.js @@ -0,0 +1,11 @@ +export function isBackendError(error) { + return error.response?.data && error.isAxiosError; +} + +export function handleBackendError(error) { + const errorData = error.response.data; + return { + message: errorData.message, + code: errorData.code, + }; +} diff --git a/components/utils/requests.js b/components/utils/requests.js new file mode 100644 index 0000000..2c6d87c --- /dev/null +++ b/components/utils/requests.js @@ -0,0 +1,44 @@ +import { API } from "aws-amplify"; +import getCurrentSessionData from "../../lib/Auth/getCurrentUser"; + +export async function getRequestWithAuth(path, data = {}) { + // const tokens = await getCurrentSessionData(); + // const jwtToken = tokens.getIdToken().getJwtToken(); + // data.headers = { + // Authorization: jwtToken, + // }; + + return API.get("CoderSpaceApi", path, data); +} + +export async function postRequestWithAuth(path, data) { + // const tokens = await getCurrentSessionData(); + // const jwtToken = tokens.getIdToken().getJwtToken(); + // data.headers = { + // Authorization: jwtToken, + // }; + + return API.post("CoderSpaceApi", path, data); +} + +export async function patchRequestWithAuth(path, data) { + // const tokens = await getCurrentSessionData(); + // const jwtToken = tokens.getIdToken().getJwtToken(); + // data.headers = { + // Authorization: jwtToken, + // }; + // console.log("data: ", data); + return API.patch("CoderSpaceApi", path, data); +} + +export async function filePostRequestWithAuth(path, data) { + // const tokens = await getCurrentSessionData(); + // const jwtToken = tokens.getIdToken().getJwtToken(); + data.headers = { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "*", + "Content-Type": "multipart/form-data", + }; + + return API.post("CoderSpaceApi", path, data); +} diff --git a/lib/Dashboards/OnlineCompiler.js b/lib/Dashboards/OnlineCompiler.js new file mode 100644 index 0000000..e887ff2 --- /dev/null +++ b/lib/Dashboards/OnlineCompiler.js @@ -0,0 +1,12 @@ +import Head from "next/head"; + +export default function OnlineCompiler() { + return ( + <> + + Online Compiler - Dashboard | CodeSpace + +

    Test and Run

    + + ); +} diff --git a/lib/Dashboards/PageNotExist.js b/lib/Dashboards/PageNotExist.js new file mode 100644 index 0000000..b8300d6 --- /dev/null +++ b/lib/Dashboards/PageNotExist.js @@ -0,0 +1,12 @@ +import Head from "next/head"; + +export default function PageDoesNotExist() { + return ( + <> + + Dashboard | CoderSpace + +

    Page does not exist

    + + ); +} diff --git a/lib/Dashboards/file/ManageFile.js b/lib/Dashboards/file/ManageFile.js new file mode 100644 index 0000000..9191838 --- /dev/null +++ b/lib/Dashboards/file/ManageFile.js @@ -0,0 +1,36 @@ +import UploadFile from "./UploadFile"; +import ViewFile from "./ViewFile"; +import { DashboardSelectionButton } from "../../../components/UI/buttons"; +import { + ButtonsContainer, + Section, +} from "../../../components/UI/Dashboard/Section"; +import PageDoesNotExist from "../PageNotExist"; + +export default function ManageFile({ tab }) { + function getSection(tab) { + if (tab === "upload") return ; + else if (tab === "view") return ; + else return ; + } + + return ( + <> + + + Upload Files + + + View Files + + +
    {getSection(tab)}
    + + ); +} diff --git a/lib/Dashboards/file/UploadFile.js b/lib/Dashboards/file/UploadFile.js new file mode 100644 index 0000000..bcc23b0 --- /dev/null +++ b/lib/Dashboards/file/UploadFile.js @@ -0,0 +1,95 @@ +import Head from "next/head"; +import FileHandler from "../../../components/UI/Files/FileHandler"; +import { FormGroup } from "../../../components/Forms/Form"; +import { useState } from "react"; +import { filePostRequestWithAuth } from "../../../components/utils/requests"; +import useAuthContext from "../../../components/contexts/useAuthContext"; +import { ButtonsContainer } from "../../../components/UI/Dashboard/Section"; +import { makeForm } from "../../../components/UI/Files/UploaderFunctions"; +import useLazyPatchRequest from "../../../components/Hooks/useLazyPatchRequest"; +import { patchRequestWithAuth } from "../../../components/utils/requests"; +import Spinner from "../../../components/UI/Spinner"; +import { ShowError } from "../../../components/UI/errors"; +import { useRouter } from "next/router"; + +export default function UploadFile() { + const router = useRouter(); + const [fileUploaderError, setFileUploaderError] = useState(null); + const [isFileUploading, setIsFileUploading] = useState(false); + const [files, setFiles] = useState([]); + // const [loading, setLoading] = useState(false); + const [disabled, setDisabled] = useState(false); + const ctx = useAuthContext(); + const { error, loading, makePatchRequest } = useLazyPatchRequest( + `/user/${ctx.userDetail.sub}`, + { + onSuccess: () => { + router.push("/dashboard/manage-file?tab=view"); + }, + } + ); + async function sendFiles(files) { + const fileResponse = []; + for (const file of files) { + const formData = { + clientName: "file", + subId: ctx.userDetail.sub, + file, + }; + const body = makeForm(formData); + const response = await uploadFile({ body }); + fileResponse.push(response); + } + return fileResponse; + } + + async function uploadFile(body) { + return await filePostRequestWithAuth("/files", body); + } + + async function fileCreationHandler(values) { + if (fileUploaderError) { + setFileUploaderError(null); + } + setIsFileUploading(true); + // setLoading(true); + try { + const body = {}; + + const fileUploadResponse = await sendFiles(files); + const fileUploadIds = fileUploadResponse.map( + (response) => response.fileSubmissionResponses[0].id + ); + setIsFileUploading(false); + body.files = fileUploadIds; + // console.log(body.files); + await makePatchRequest(body); + // await patchRequestWithAuth({ files: body.files }); + } catch (err) { + console.error(err); + setFileUploaderError(err); + setIsFileUploading(false); + } + } + return ( + <> + + Files - Dashboard | CodeSpace + +

    Upload Files

    + + + + + + + + ); +} diff --git a/lib/Dashboards/file/ViewFile.js b/lib/Dashboards/file/ViewFile.js new file mode 100644 index 0000000..8260944 --- /dev/null +++ b/lib/Dashboards/file/ViewFile.js @@ -0,0 +1,12 @@ +import useAuthContext from "../../../components/contexts/useAuthContext"; +import useGetRequest from "../../../components/Hooks/useGetRequest"; +import ShowFiles from "../../../components/UI/Files/ShowFiles"; +export default function ViewFile() { + const { userDetail } = useAuthContext(); + + const { data, error, loading } = useGetRequest(`/user/${userDetail.sub}`); + // const value = data.user; + // console.log(data.user); + console.log(data); + return <>{/* */}; +} diff --git a/lib/Dashboards/profile/EditProfile.js b/lib/Dashboards/profile/EditProfile.js new file mode 100644 index 0000000..5688b92 --- /dev/null +++ b/lib/Dashboards/profile/EditProfile.js @@ -0,0 +1,12 @@ +import Head from "next/head"; + +export default function EditProfile() { + return ( + <> + + Profile - Dashboard | CodeSpace + +

    Edit Profile

    + + ); +} diff --git a/lib/Dashboards/profile/ProfileSelection.js b/lib/Dashboards/profile/ProfileSelection.js new file mode 100644 index 0000000..a55d340 --- /dev/null +++ b/lib/Dashboards/profile/ProfileSelection.js @@ -0,0 +1,55 @@ +import { useEffect, useState } from "react"; + +import ViewProfile from "./ViewProfile"; +import EditProfile from "./EditProfile"; +import { Section } from "../../../components/UI/Dashboard/Section"; +import PageDoesNotExist from "../../../lib/Dashboards/PageNotExist"; +import useGetRequest from "../../../components/Hooks/useGetRequest"; +import useAuthContext from "../../../components/contexts/useAuthContext"; + +export default function Profile({ tab }) { + const [profileData, setProfileData] = useState({}); + + const { userDetail } = useAuthContext(); + const { + data: user, + error, + loading, + } = useGetRequest(`/user/${userDetail.sub}`, { + initialDataState: { + user: {}, + }, + }); + // console.log(user); + useEffect(() => { + setProfileData(user); + }, [user]); + + function updateLatestProfileData(value) { + setProfileData((prev) => ({ + ...prev, + ...value, + })); + } + + function getSection(tab) { + if (tab === "view") + return ( + + ); + else if (tab === "edit") + return ( + + ); + else return ; + } + + return
    {getSection(tab)}
    ; +} diff --git a/lib/Dashboards/profile/ViewProfile.js b/lib/Dashboards/profile/ViewProfile.js new file mode 100644 index 0000000..7885ac7 --- /dev/null +++ b/lib/Dashboards/profile/ViewProfile.js @@ -0,0 +1,35 @@ +import Link from "next/link"; + +import { ShowError } from "../../../components/UI/errors"; +import Spinner from "../../../components/UI/Spinner"; + +export default function ViewProfile({ profileData, error, loading }) { + console.log(profileData); + return ( +
    +

    + Your profile +

    + + + {loading || error ? null : !Object.keys(profileData.user) + .length ? null : ( + <> +
    +

    + Name: + {profileData.user[0].name} +

    +

    + Username: + {profileData.user[0].username} +

    +
    + + Edit + + + )} +
    + ); +} diff --git a/lib/Hooks/useSignup.js b/lib/Hooks/useSignup.js index af713e8..d1e9fcc 100644 --- a/lib/Hooks/useSignup.js +++ b/lib/Hooks/useSignup.js @@ -2,6 +2,7 @@ import { useState, useCallback } from "react"; import signUp from "../Auth/signup"; import useIsMounted from "./useIsMounted"; +import { postRequestWithAuth } from "../../components/utils/requests"; export default function useSignup(options) { const isMounted = useIsMounted(); @@ -27,6 +28,9 @@ export default function useSignup(options) { data.lastName ); console.log(response); + await postRequestWithAuth(`/user/${response.UserSub}`, { + name: data.firstName, + }); if (isMounted.current) { setLoading(false); options.onSuccess(); diff --git a/package.json b/package.json index 95d2543..c8eb2ed 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "aws-amplify": "^4.3.16", + "filesize": "^8.0.7", "formik": "^2.2.9", "next": "12.1.0", "react": "17.0.2", diff --git a/pages/confirm-message.js b/pages/confirm-message.js index 73dd0f6..52b8e4d 100644 --- a/pages/confirm-message.js +++ b/pages/confirm-message.js @@ -4,7 +4,7 @@ export default function ConfirmMessage() { return ( <> - Confirm Message | SourceQwik + Confirm Message | CodeSpace

    Welcome!

    diff --git a/pages/dashboard.js b/pages/dashboard.js new file mode 100644 index 0000000..fada6e7 --- /dev/null +++ b/pages/dashboard.js @@ -0,0 +1,17 @@ +import { useRouter } from "next/router"; +import { useEffect } from "react"; + +import useAuthContext from "../components/contexts/useAuthContext"; + +export default function Dashboard() { + const router = useRouter(); + const { identity } = useAuthContext(); + + useEffect(() => { + router.push("dashboard/profile?tab=view"); + }); + + return null; +} + +// Dashboard.requireAuth = true; diff --git a/pages/dashboard/[type].js b/pages/dashboard/[type].js new file mode 100644 index 0000000..62b7c07 --- /dev/null +++ b/pages/dashboard/[type].js @@ -0,0 +1,81 @@ +import { useRouter } from "next/router"; +import Head from "next/head"; +import { FaFileAlt, FaUser, FaCode } from "react-icons/fa"; + +import { DashboardSidebarButton } from "../../components/UI/buttons"; +import Sidebar from "../../components/UI/Dashboard/Sidebar"; +import DashboardSelection from "../../components/UI/Dashboard/DashboardSelection"; +import Dashboard from "../../components/UI/Dashboard/Dashboard"; +import ManageFile from "../../lib/Dashboards/file/ManageFile"; +import ProfileSelection from "../../lib/Dashboards/profile/ProfileSelection"; +import OnlineCompiler from "../../lib/Dashboards/OnlineCompiler"; +import PageDoesNotExist from "../../lib/Dashboards/PageNotExist"; + +export default function DashboardPage() { + const router = useRouter(); + const { type, tab } = router.query; + + function getSelection(type) { + if (type === "manage-file") { + return ( + <> + + Files - Dashboard | CodeSpace + + + + ); + } else if (type === "profile") { + return ( + <> + + Profile - Dashboard | CodeSpace + + + + ); + } else if (type === "compiler") { + return ( + <> + + Online Compiler - Dashboard | CodeSpace + + + + ); + } else { + return ; + } + } + + return ( + + + + + Upload File + + + + Profile + + + + Online compiler + + + {getSelection(type)} + + ); +} + +// DashboardPage.requireAuth = true; diff --git a/pages/index.js b/pages/index.js index b526aab..080232f 100644 --- a/pages/index.js +++ b/pages/index.js @@ -18,29 +18,34 @@ export default function Home() {

    Get started by creating your account{" "} - pages/index.js + Hello World!

    diff --git a/pages/welcome.js b/pages/welcome.js index f43bf5b..2d80495 100644 --- a/pages/welcome.js +++ b/pages/welcome.js @@ -8,7 +8,7 @@ export default function Welcome() { Your email address has been successfully verified.

    - Click here to login + Click here to login
    ); diff --git a/src/config.js b/src/config.js index fb22541..a73ffbd 100644 --- a/src/config.js +++ b/src/config.js @@ -4,6 +4,14 @@ const config = { userPoolId: process.env.NEXT_PUBLIC_USER_POOL_ID, userPoolWebClientId: process.env.NEXT_PUBLIC_USER_POOL_WEB_CLIENT_ID, }, + API: { + endpoints: [ + { + name: "CoderSpaceApi", + endpoint: "http://localhost:8000/api", + }, + ], + }, }; export default config; diff --git a/styles/globals.css b/styles/globals.css index 9d0adeb..4a8ae85 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -256,5 +256,5 @@ body { } .nav-btn { - @apply bg-green text-white rounded text-sm font-bold py-2 px-3 hover:bg-hoverGreen disabled:bg-red-500; + @apply bg-slate-700 text-white rounded text-sm font-bold py-2 px-3 hover:bg-slate-500 disabled:bg-red-500; } diff --git a/tailwind.config.js b/tailwind.config.js index 0be71ab..08b8449 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -14,6 +14,9 @@ module.exports = { gray: { DEFAULT: "#1e1e1e", }, + csBlue: { + DEFAULT: "#0070f3", + }, lightGray: { DEFAULT: "#4c565a", }, From a17998d6b83931951f605af9de82dbe207f4294d Mon Sep 17 00:00:00 2001 From: sabreen-parveen Date: Thu, 12 May 2022 15:50:52 +0530 Subject: [PATCH 2/5] visualizer --- .vscode/settings.json | 3 + components/Forms/HandleForm.js | 24 ++++ components/Hooks/useGetRequestWithoutAuth.js | 2 +- .../Hooks/useLazyGetRequestWithoutAuth.js | 2 +- components/UI/ProgressBar.js | 35 +++++ components/UI/buttons.js | 6 +- components/utils/requests.js | 2 +- .../Analytics/AnalyticsSelection.js | 33 +++++ .../Analytics/Codeforces/CodeforcesCompare.js | 1 + .../Codeforces/CodeforcesPersonalProfile.js | 10 ++ lib/Dashboards/Analytics/Leetcode.js | 128 ++++++++++++++++++ lib/Hooks/useSignup.js | 15 +- next.config.js | 13 +- package.json | 2 + pages/cfviz | 1 + pages/dashboard/[type].js | 14 +- pages/login.js | 1 + styles/globals.css | 2 +- tailwind.config.js | 3 + 19 files changed, 280 insertions(+), 17 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 components/Forms/HandleForm.js create mode 100644 components/UI/ProgressBar.js create mode 100644 lib/Dashboards/Analytics/AnalyticsSelection.js create mode 100644 lib/Dashboards/Analytics/Codeforces/CodeforcesCompare.js create mode 100644 lib/Dashboards/Analytics/Codeforces/CodeforcesPersonalProfile.js create mode 100644 lib/Dashboards/Analytics/Leetcode.js create mode 160000 pages/cfviz diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6b665aa --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} diff --git a/components/Forms/HandleForm.js b/components/Forms/HandleForm.js new file mode 100644 index 0000000..72b13d6 --- /dev/null +++ b/components/Forms/HandleForm.js @@ -0,0 +1,24 @@ +import { Field, Form, Formik, ErrorMessage } from "formik"; +import { FormSubmitBtn } from "../UI/buttons"; +import { FormGroup } from "./Form"; + +export default function HandleForm({ submitHandler }) { + return ( +
    +
    + + + +
    +
    + ); +} diff --git a/components/Hooks/useGetRequestWithoutAuth.js b/components/Hooks/useGetRequestWithoutAuth.js index 962b999..f5759a0 100644 --- a/components/Hooks/useGetRequestWithoutAuth.js +++ b/components/Hooks/useGetRequestWithoutAuth.js @@ -13,7 +13,7 @@ export default function useGetRequestWithoutAuth(route, options = {}) { useEffect(() => { async function makeRequest() { try { - const response = await API.get("sourceQwik", route); + const response = await API.get("coderSpace", route); if (isMounted.current) { setLoading(false); setData(response); diff --git a/components/Hooks/useLazyGetRequestWithoutAuth.js b/components/Hooks/useLazyGetRequestWithoutAuth.js index 574d168..1213e28 100644 --- a/components/Hooks/useLazyGetRequestWithoutAuth.js +++ b/components/Hooks/useLazyGetRequestWithoutAuth.js @@ -18,7 +18,7 @@ export default function useLazyGetRequestWithoutAuth(options = {}) { } setLoading(true); try { - const response = await API.get("sourceQwik", route); + const response = await API.get("coderSpace", route); setLoading(false); setData(response); if (options.onSuccess) { diff --git a/components/UI/ProgressBar.js b/components/UI/ProgressBar.js new file mode 100644 index 0000000..aa02c7e --- /dev/null +++ b/components/UI/ProgressBar.js @@ -0,0 +1,35 @@ +import React from "react"; + +const ProgressBar = ({ bgcolor, progress, height }) => { + const Parentdiv = { + height: 15, + width: 500, + backgroundColor: "whitesmoke", + borderRadius: 20, + margin: 20, + }; + + const Childdiv = { + height: "100%", + width: `${progress}%`, + backgroundColor: bgcolor, + borderRadius: 10, + textAlign: "right", + }; + + const progresstext = { + padding: 10, + color: "black", + fontWeight: 900, + }; + + return ( +
    +
    + +
    +
    + ); +}; + +export default ProgressBar; diff --git a/components/UI/buttons.js b/components/UI/buttons.js index a1db27a..3916b77 100644 --- a/components/UI/buttons.js +++ b/components/UI/buttons.js @@ -2,7 +2,11 @@ import router from "next/router"; export function FormSubmitBtn({ children, type, disabled }) { return ( - ); diff --git a/components/utils/requests.js b/components/utils/requests.js index 2c6d87c..0719823 100644 --- a/components/utils/requests.js +++ b/components/utils/requests.js @@ -17,7 +17,7 @@ export async function postRequestWithAuth(path, data) { // data.headers = { // Authorization: jwtToken, // }; - + console.log(data); return API.post("CoderSpaceApi", path, data); } diff --git a/lib/Dashboards/Analytics/AnalyticsSelection.js b/lib/Dashboards/Analytics/AnalyticsSelection.js new file mode 100644 index 0000000..b230a10 --- /dev/null +++ b/lib/Dashboards/Analytics/AnalyticsSelection.js @@ -0,0 +1,33 @@ +import { Section } from "../../../components/UI/Dashboard/Section"; +import PageDoesNotExist from "../../../lib/Dashboards/PageNotExist"; +import LeetcodeProfile from "./Leetcode"; +import CodeforcesPersonalProfile from "./Codeforces/CodeforcesPersonalProfile"; +import { ButtonsContainer } from "../../../components/UI/Dashboard/Section"; +import { DashboardSelectionButton } from "../../../components/UI/buttons"; + +export default function Analytics({ tab }) { + function getSection(tab) { + if (tab === "codeforces") return ; + else if (tab === "leetcode") return ; + else return ; + } + return ( + <> + + + CodeForces + + + Leetcode + + +
    {getSection(tab)}
    + + ); +} diff --git a/lib/Dashboards/Analytics/Codeforces/CodeforcesCompare.js b/lib/Dashboards/Analytics/Codeforces/CodeforcesCompare.js new file mode 100644 index 0000000..3aa6a64 --- /dev/null +++ b/lib/Dashboards/Analytics/Codeforces/CodeforcesCompare.js @@ -0,0 +1 @@ +export default function CodeforcesCompare() {} diff --git a/lib/Dashboards/Analytics/Codeforces/CodeforcesPersonalProfile.js b/lib/Dashboards/Analytics/Codeforces/CodeforcesPersonalProfile.js new file mode 100644 index 0000000..9815747 --- /dev/null +++ b/lib/Dashboards/Analytics/Codeforces/CodeforcesPersonalProfile.js @@ -0,0 +1,10 @@ +import Head from "next/head"; +import { useRouter } from "next/router"; +import { useEffect } from "react"; +export default function CodeforcesPersonalProfile() { + const router = useRouter(); + useEffect(() => { + router.push("/analytics/codeforces"); + }); + return null; +} diff --git a/lib/Dashboards/Analytics/Leetcode.js b/lib/Dashboards/Analytics/Leetcode.js new file mode 100644 index 0000000..2e1cfce --- /dev/null +++ b/lib/Dashboards/Analytics/Leetcode.js @@ -0,0 +1,128 @@ +import Head from "next/head"; +import { Chart as ChartJS, ArcElement, Tooltip, Legend } from "chart.js"; +import { Doughnut } from "react-chartjs-2"; +ChartJS.register(ArcElement, Tooltip, Legend); +import { getRequestWithAuth } from "../../../components/utils/requests"; +import { useState } from "react"; +import HandleForm from "../../../components/Forms/HandleForm"; +import ProgressBar from "../../../components/UI/ProgressBar"; + +const progressData = {}; +const data = { + labels: [], + datasets: [ + { + label: "# of Votes", + backgroundColor: ["rgba(255, 161, 23, 1)", "rgb(211,211,211)"], + borderColor: ["rgba(255, 161, 23, 1)", "rgb(211,211,211)"], + borderWidth: 10, + }, + ], +}; +const options = { + elements: { + arc: { + weight: 0.5, + borderWidth: 3, + }, + }, + cutout: 150, +}; + +export default function LeetcodeProfile() { + const [error, setError] = useState(null); + const [handle, setHandle] = useState(false); + const [loading, setLoading] = useState(false); + + // const value = await getRequestWithAuth(`ananalytics/leetcode/${username}`) + async function submitHandler(username) { + console.log(username); + setLoading(true); + if (error) { + setError(null); + } + + try { + const value = await getRequestWithAuth( + `/analytics/leetcode/${username.handle}` + ); + data.datasets[0].data = [ + value.getLeetcodeProfile.totalSolved, + value.getLeetcodeProfile.totalQuestions, + ]; + progressData.easyPerc = + (value.getLeetcodeProfile.easySolved / + value.getLeetcodeProfile.totalEasy) * + 100; + progressData.mediumPerc = + (value.getLeetcodeProfile.mediumSolved / + value.getLeetcodeProfile.totalMedium) * + 100; + progressData.hardPerc = + (value.getLeetcodeProfile.hardSolved / + value.getLeetcodeProfile.totalHard) * + 100; + setHandle(true); + console.log(value); + } catch (err) { + setError(err); + setLoading(false); + } + } + return ( + <> + + Analytics - Dashboard | CodeSpace + +

    Leetcode

    + + {handle == true ? ( +
    +
    +
    + +
    +
    +
    +
    Easy
    + +
    +
    +
    + Medium + +
    + +
    +
    +
    Hard
    + +
    +
    +
    +
    + ) : null} + + ); +} diff --git a/lib/Hooks/useSignup.js b/lib/Hooks/useSignup.js index d1e9fcc..913eaa7 100644 --- a/lib/Hooks/useSignup.js +++ b/lib/Hooks/useSignup.js @@ -28,9 +28,18 @@ export default function useSignup(options) { data.lastName ); console.log(response); - await postRequestWithAuth(`/user/${response.UserSub}`, { - name: data.firstName, - }); + const user = { + body: { + name: data.firstName + " " + data.lastName, + }, + }; + await postRequestWithAuth(`/user/${response.userSub}`, user); + // const res = await postRequestWithAuth(`/user/asus`, { + // body: { + // name: "abcdef", + // }, + // }); + // console.log(res); if (isMounted.current) { setLoading(false); options.onSuccess(); diff --git a/next.config.js b/next.config.js index a843cbe..16c008a 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,15 @@ /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, -} + async redirects() { + return [ + { + source: "/analytics/codeforces", + destination: "http://127.0.0.1:5501/pages/cfviz/index.html", + permanent: true, + }, + ]; + }, +}; -module.exports = nextConfig +module.exports = nextConfig; diff --git a/package.json b/package.json index c8eb2ed..6d2a5b8 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,12 @@ }, "dependencies": { "aws-amplify": "^4.3.16", + "chart.js": "^3.7.1", "filesize": "^8.0.7", "formik": "^2.2.9", "next": "12.1.0", "react": "17.0.2", + "react-chartjs-2": "^4.1.0", "react-dom": "17.0.2", "react-icons": "^4.3.1", "react-loader-spinner": "^6.0.0-0", diff --git a/pages/cfviz b/pages/cfviz new file mode 160000 index 0000000..b31e9fb --- /dev/null +++ b/pages/cfviz @@ -0,0 +1 @@ +Subproject commit b31e9fb1366873d6d719a0d276debc7d5ec74970 diff --git a/pages/dashboard/[type].js b/pages/dashboard/[type].js index 62b7c07..160d822 100644 --- a/pages/dashboard/[type].js +++ b/pages/dashboard/[type].js @@ -8,8 +8,8 @@ import DashboardSelection from "../../components/UI/Dashboard/DashboardSelection import Dashboard from "../../components/UI/Dashboard/Dashboard"; import ManageFile from "../../lib/Dashboards/file/ManageFile"; import ProfileSelection from "../../lib/Dashboards/profile/ProfileSelection"; -import OnlineCompiler from "../../lib/Dashboards/OnlineCompiler"; import PageDoesNotExist from "../../lib/Dashboards/PageNotExist"; +import Analytics from "../../lib/Dashboards/Analytics/AnalyticsSelection"; export default function DashboardPage() { const router = useRouter(); @@ -34,13 +34,13 @@ export default function DashboardPage() { ); - } else if (type === "compiler") { + } else if (type === "analytics") { return ( <> - Online Compiler - Dashboard | CodeSpace + Analytics - Dashboard | CodeSpace - + ); } else { @@ -66,11 +66,11 @@ export default function DashboardPage() { Profile - Online compiler + Analytics {getSelection(type)} diff --git a/pages/login.js b/pages/login.js index 0b26afa..5106c55 100644 --- a/pages/login.js +++ b/pages/login.js @@ -27,6 +27,7 @@ export default function Login() { ctx.changeUserDetail(user.attributes); ctx.changeLoginState(true); setLoading(false); + router.push("/dashboard"); } catch (err) { let error = err; let errorMessage = "Something happened. Please try again."; diff --git a/styles/globals.css b/styles/globals.css index 4a8ae85..10a31ff 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -230,7 +230,7 @@ body { } .form-button { - @apply bg-gray-100 text-lg font-medium font-bold text-white w-96 rounded-lg h-12 my-2 hover:bg-hoverGreen focus:outline-none; + @apply bg-slate-700 text-lg font-medium font-bold text-white w-96 rounded-lg h-12 my-2 hover:bg-hoverGreen focus:outline-none; } .rfq-form-label { diff --git a/tailwind.config.js b/tailwind.config.js index 08b8449..9090f8f 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -26,6 +26,9 @@ module.exports = { hoverGreen: { DEFAULT: "#243737", }, + blueGray: { + DEFAULT: "#64748B", + }, }, }, }, From 95895a92f5e1322c4e02f2624847fcdedaa4f8f3 Mon Sep 17 00:00:00 2001 From: sabreen-parveen Date: Fri, 13 May 2022 00:50:34 +0530 Subject: [PATCH 3/5] fix-leetcode --- components/Forms/HandleForm.js | 44 +++++++++----- components/UI/Todo/DeleteIcon.js | 10 ++++ components/UI/Todo/EditIcon.js | 10 ++++ components/UI/Todo/Todo.js | 71 ++++++++++++++++++++++ lib/Dashboards/Analytics/Leetcode.js | 88 +++++++++++++++------------- lib/Dashboards/Todo/TodoWindow.js | 58 ++++++++++++++++++ package.json | 1 + pages/dashboard/[type].js | 17 ++++++ 8 files changed, 242 insertions(+), 57 deletions(-) create mode 100644 components/UI/Todo/DeleteIcon.js create mode 100644 components/UI/Todo/EditIcon.js create mode 100644 components/UI/Todo/Todo.js create mode 100644 lib/Dashboards/Todo/TodoWindow.js diff --git a/components/Forms/HandleForm.js b/components/Forms/HandleForm.js index 72b13d6..9c2580a 100644 --- a/components/Forms/HandleForm.js +++ b/components/Forms/HandleForm.js @@ -4,21 +4,33 @@ import { FormGroup } from "./Form"; export default function HandleForm({ submitHandler }) { return ( -
    -
    - - - -
    -
    + + {({ isSubmitting }) => ( +
    +
    +
    + + + + +
    +
    +
    + )} +
    ); } diff --git a/components/UI/Todo/DeleteIcon.js b/components/UI/Todo/DeleteIcon.js new file mode 100644 index 0000000..b84acc9 --- /dev/null +++ b/components/UI/Todo/DeleteIcon.js @@ -0,0 +1,10 @@ +const EditIcon = props=>{ + return( + + + + + ) +} + +export default EditIcon; diff --git a/components/UI/Todo/EditIcon.js b/components/UI/Todo/EditIcon.js new file mode 100644 index 0000000..b84acc9 --- /dev/null +++ b/components/UI/Todo/EditIcon.js @@ -0,0 +1,10 @@ +const EditIcon = props=>{ + return( + + + + + ) +} + +export default EditIcon; diff --git a/components/UI/Todo/Todo.js b/components/UI/Todo/Todo.js new file mode 100644 index 0000000..3c01b10 --- /dev/null +++ b/components/UI/Todo/Todo.js @@ -0,0 +1,71 @@ +import React, { useState } from "react"; +import DeleteIcon from "./DeleteIcon"; +import EditIcon from "./EditIcon"; + +const Todo = ({ text, id, completed, todos, setTodos }) => { + const [editMode, setEditMode] = useState(false); + const [editVal, setEditVal] = useState(text); + const deleteTodo = (e) => { + let newTodos = [...todos]; + newTodos = newTodos.filter((el) => el.id !== id); + setTodos(newTodos); + }; + const handleEdit = (e) => { + e.preventDefault(); + const newTodos = [...todos]; + const index = newTodos.findIndex((el) => el.id === id); + newTodos[index] = { + ...newTodos[index], + text: editVal, + }; + setTodos(newTodos); + setEditMode(false); + }; + const handleCheckBoxToggle = (e) => { + const newTodos = [...todos]; + const index = newTodos.findIndex((el) => el.id === id); + newTodos[index] = { + ...newTodos[index], + completed: e.currentTarget.checked, + }; + setTodos(newTodos); + setEditMode(false); + }; + return ( +
    +
    + + {editMode ? ( +
    + {" "} + setEditVal(e.target.value)} + /> +
    + ) : ( +
    {text}
    + )} +
    + +
    + setEditMode(true)} + /> + +
    +
    + ); +}; + +export default Todo; diff --git a/lib/Dashboards/Analytics/Leetcode.js b/lib/Dashboards/Analytics/Leetcode.js index 2e1cfce..cc5af14 100644 --- a/lib/Dashboards/Analytics/Leetcode.js +++ b/lib/Dashboards/Analytics/Leetcode.js @@ -37,19 +37,23 @@ export default function LeetcodeProfile() { // const value = await getRequestWithAuth(`ananalytics/leetcode/${username}`) async function submitHandler(username) { console.log(username); + setHandle(false); setLoading(true); if (error) { setError(null); } try { + console.log(username); const value = await getRequestWithAuth( `/analytics/leetcode/${username.handle}` ); + console.log(value); data.datasets[0].data = [ value.getLeetcodeProfile.totalSolved, value.getLeetcodeProfile.totalQuestions, ]; + console.log(data.datasets[0].data); progressData.easyPerc = (value.getLeetcodeProfile.easySolved / value.getLeetcodeProfile.totalEasy) * @@ -75,54 +79,56 @@ export default function LeetcodeProfile() { Analytics - Dashboard | CodeSpace

    Leetcode

    - - {handle == true ? ( -
    -
    -
    - -
    -
    -
    -
    Easy
    - + + {handle == true ? ( +
    +
    +
    +
    -
    +
    - Medium - +
    Easy
    + +
    +
    +
    + Medium + +
    + +
    +
    +
    Hard
    +
    - -
    -
    -
    Hard
    -
    -
    - ) : null} + ) : null} +
    ); } diff --git a/lib/Dashboards/Todo/TodoWindow.js b/lib/Dashboards/Todo/TodoWindow.js new file mode 100644 index 0000000..3e4ef2c --- /dev/null +++ b/lib/Dashboards/Todo/TodoWindow.js @@ -0,0 +1,58 @@ +import { useState } from "react"; +import uniqid from "uniqid"; +import Todo from "../../../components/UI/Todo/Todo"; + +const TodoWindow = (props) => { + const [value, setValue] = useState(""); + const [todos, setTodos] = useState([]); + const handleAddTodo = (e) => { + e.preventDefault(); + if (value === "") return; + const newTodos = [...todos]; + + newTodos.push({ + id: uniqid(), + completed: false, + text: value, + }); + setTodos(newTodos); + setValue(""); + }; + + return ( +
    +
    +

    Todo List

    +
    + setValue(e.target.value)} + /> + +
    +
    + {todos.map(({ id, text, completed }) => { + return ( + + ); + })} +
    +
    +
    + ); +}; + +export default TodoWindow; diff --git a/package.json b/package.json index 6d2a5b8..657dfb8 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "react-icons": "^4.3.1", "react-loader-spinner": "^6.0.0-0", "react-phone-input-2": "^2.15.0", + "uniqid": "^5.4.0", "yup": "^0.32.11" }, "devDependencies": { diff --git a/pages/dashboard/[type].js b/pages/dashboard/[type].js index 160d822..6bc9e74 100644 --- a/pages/dashboard/[type].js +++ b/pages/dashboard/[type].js @@ -10,6 +10,7 @@ import ManageFile from "../../lib/Dashboards/file/ManageFile"; import ProfileSelection from "../../lib/Dashboards/profile/ProfileSelection"; import PageDoesNotExist from "../../lib/Dashboards/PageNotExist"; import Analytics from "../../lib/Dashboards/Analytics/AnalyticsSelection"; +import TodoWindow from "../../lib/Dashboards/Todo/TodoWindow"; export default function DashboardPage() { const router = useRouter(); @@ -43,6 +44,15 @@ export default function DashboardPage() { ); + } else if (type === "todo") { + return ( + <> + + Todo - Dashboard | CodeSpace + + + + ); } else { return ; } @@ -72,6 +82,13 @@ export default function DashboardPage() { Analytics + + + Todo + {getSelection(type)} From 3338cbc8d8e73f8cbcdf48caa202a6b3cf15bc8c Mon Sep 17 00:00:00 2001 From: sabreen-parveen Date: Fri, 13 May 2022 10:29:51 +0530 Subject: [PATCH 4/5] fix --- components/UI/ProgressBar.js | 3 ++- lib/Dashboards/Analytics/Leetcode.js | 34 +++++++++++++++++++++------- lib/Dashboards/Todo/TodoWindow.js | 6 ++--- tailwind.config.js | 3 +++ 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/components/UI/ProgressBar.js b/components/UI/ProgressBar.js index aa02c7e..d1c9507 100644 --- a/components/UI/ProgressBar.js +++ b/components/UI/ProgressBar.js @@ -6,7 +6,8 @@ const ProgressBar = ({ bgcolor, progress, height }) => { width: 500, backgroundColor: "whitesmoke", borderRadius: 20, - margin: 20, + marginTop: 10, + marginBottom: 10, }; const Childdiv = { diff --git a/lib/Dashboards/Analytics/Leetcode.js b/lib/Dashboards/Analytics/Leetcode.js index cc5af14..a36d92e 100644 --- a/lib/Dashboards/Analytics/Leetcode.js +++ b/lib/Dashboards/Analytics/Leetcode.js @@ -66,6 +66,18 @@ export default function LeetcodeProfile() { (value.getLeetcodeProfile.hardSolved / value.getLeetcodeProfile.totalHard) * 100; + progressData.easy = + value.getLeetcodeProfile.easySolved + + "/" + + value.getLeetcodeProfile.totalEasy; + progressData.medium = + value.getLeetcodeProfile.mediumSolved + + "/" + + value.getLeetcodeProfile.totalMedium; + progressData.hard = + value.getLeetcodeProfile.hardSolved + + "/" + + value.getLeetcodeProfile.totalHard; setHandle(true); console.log(value); } catch (err) { @@ -83,8 +95,8 @@ export default function LeetcodeProfile() { {handle == true ? (
    -
    -
    +
    +
    -
    +
    -
    Easy
    +
    +
    Easy
    +
    {progressData.easy}
    +
    -
    - Medium - +
    +
    Medium
    +
    {progressData.medium}
    -
    Hard
    +
    +
    Hard
    +
    {progressData.hard}
    +
    { }; return ( -
    -
    +
    +

    Todo List

    setValue(e.target.value)} diff --git a/tailwind.config.js b/tailwind.config.js index 9090f8f..3026209 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -7,6 +7,9 @@ module.exports = { ], theme: { extend: { + height: { + 128: "32rem", + }, colors: { green: { DEFAULT: "#2c4444", From ca0f219efe38589881f64a1505f2eac2400c21e0 Mon Sep 17 00:00:00 2001 From: sabreen-parveen Date: Sat, 14 May 2022 11:31:06 +0530 Subject: [PATCH 5/5] changes --- components/Forms/HandleForm.js | 2 +- .../Hooks/useFileGetRequestWithoutAuth.js | 2 +- components/UI/Files/ShowFiles.js | 4 +- components/UI/Todo/Todo.js | 6 +- components/UI/carousel.js | 78 ++++++++++++++++++ lib/Dashboards/Analytics/Leetcode.js | 76 +++++++++-------- lib/Dashboards/file/ViewFile.js | 32 ++++++- package.json | 2 + pages/dashboard/[type].js | 6 +- pages/index.js | 36 +------- public/images/analytics.png | Bin 0 -> 4067 bytes public/images/file-upload.jpg | Bin 0 -> 71011 bytes public/images/file.jpg | Bin 0 -> 3147 bytes public/images/todo.png | Bin 0 -> 4059 bytes styles/Home.module.css | 2 +- styles/globals.css | 1 + 16 files changed, 162 insertions(+), 85 deletions(-) create mode 100644 components/UI/carousel.js create mode 100644 public/images/analytics.png create mode 100644 public/images/file-upload.jpg create mode 100644 public/images/file.jpg create mode 100644 public/images/todo.png diff --git a/components/Forms/HandleForm.js b/components/Forms/HandleForm.js index 9c2580a..c8951fc 100644 --- a/components/Forms/HandleForm.js +++ b/components/Forms/HandleForm.js @@ -13,7 +13,7 @@ export default function HandleForm({ submitHandler }) { {({ isSubmitting }) => (
    -
    +
    diff --git a/components/Hooks/useFileGetRequestWithoutAuth.js b/components/Hooks/useFileGetRequestWithoutAuth.js index 3ff15bf..f66f908 100644 --- a/components/Hooks/useFileGetRequestWithoutAuth.js +++ b/components/Hooks/useFileGetRequestWithoutAuth.js @@ -16,7 +16,7 @@ export default function useFileGetRequestWithoutAuth(route, options = {}) { useEffect(() => { async function makeRequest() { try { - const response = await API.get("Diogenes", route); + const response = await API.get("CoderSpaceApi", route); if (isMounted.current) { setLoading(false); setData(response); diff --git a/components/UI/Files/ShowFiles.js b/components/UI/Files/ShowFiles.js index c32a44b..6b77701 100644 --- a/components/UI/Files/ShowFiles.js +++ b/components/UI/Files/ShowFiles.js @@ -1,6 +1,6 @@ import Spinner from "../Spinner"; import { ShowError } from "../errors"; -import useFileGetRequestWithoutAuth from "../../../components/Hooks/useFileGetRequestWithoutAuth"; +import useFileGetRequestWithoutAuth from "../../Hooks/useFileGetRequestWithoutAuth"; export default function ShowFiles({ fileIds, additionalData }) { return ( @@ -43,7 +43,7 @@ function ShowFileData({ data, showAdditionalData = false }) {

    ) : null} { @@ -59,8 +59,8 @@ const Todo = ({ text, id, completed, todos, setTodos }) => { className="w-6 hover:cursor-pointer transition-all hover:opacity-80" onClick={(e) => setEditMode(true)} /> -
    diff --git a/components/UI/carousel.js b/components/UI/carousel.js new file mode 100644 index 0000000..6baca17 --- /dev/null +++ b/components/UI/carousel.js @@ -0,0 +1,78 @@ +import React, { Component } from "react"; +import ReactDOM from "react-dom"; +import ReactCardCarousel from "react-card-carousel"; +import { FaChartPie, FaChartLine, FaChartBar } from "react-icons/fa"; +import analytics from "../../public/images/analytics.png"; +import file from "../../public/images/file.jpg"; +import todo from "../../public/images/todo.png"; + +import Image from "next/image"; +class MyCarousel extends Component { + static get CONTAINER_STYLE() { + return { + position: "relative", + height: "100vh", + width: "100%", + display: "flex", + flex: 1, + justifyContent: "center", + alignItems: "middle", + }; + } + + static get CARD_STYLE() { + return { + height: "500px", + width: "500px", + paddingTop: "80px", + padding: "50px", + textAlign: "center", + background: "#334155", + color: "#FFF", + fontFamily: "sans-serif", + fontSize: "20px", + textTransform: "", + borderRadius: "10px", + boxSizing: "border-box", + }; + } + + render() { + console.log(analytics); + + return ( +
    + +
    + Analyse your performance +
    + analytics +
    + Are you a competitive programmer? Do you want to improve yourself + by comapring your performance with different users. +
    +
    +
    + Upload and view your files +
    + file uploader +
    + Manage all your documents at one place. Upload your files to a + secured cloud storage. +
    +
    +
    + Manage your daily work +
    + todo-list +
    + Manage your daya to day work by preparing a todo list. +
    +
    +
    +
    + ); + } +} + +export default MyCarousel; diff --git a/lib/Dashboards/Analytics/Leetcode.js b/lib/Dashboards/Analytics/Leetcode.js index a36d92e..499d47a 100644 --- a/lib/Dashboards/Analytics/Leetcode.js +++ b/lib/Dashboards/Analytics/Leetcode.js @@ -95,8 +95,8 @@ export default function LeetcodeProfile() { {handle == true ? (
    -
    -
    +
    +
    -
    -
    -
    -
    Easy
    -
    {progressData.easy}
    +
    +
    +
    +
    +
    Easy
    +
    {progressData.easy}
    +
    +
    - -
    -
    -
    -
    Medium
    -
    {progressData.medium}
    +
    +
    +
    Medium
    +
    {progressData.medium}
    +
    +
    - -
    -
    -
    -
    Hard
    -
    {progressData.hard}
    +
    +
    +
    Hard
    +
    {progressData.hard}
    +
    + +
    -
    +
    ) : null} diff --git a/lib/Dashboards/file/ViewFile.js b/lib/Dashboards/file/ViewFile.js index 8260944..3d6b3b6 100644 --- a/lib/Dashboards/file/ViewFile.js +++ b/lib/Dashboards/file/ViewFile.js @@ -1,12 +1,36 @@ import useAuthContext from "../../../components/contexts/useAuthContext"; import useGetRequest from "../../../components/Hooks/useGetRequest"; +import { getRequestWithAuth } from "../../../components/utils/requests"; import ShowFiles from "../../../components/UI/Files/ShowFiles"; +import { useEffect, useState } from "react"; + export default function ViewFile() { + const [profileData, setProfileData] = useState({}); const { userDetail } = useAuthContext(); - - const { data, error, loading } = useGetRequest(`/user/${userDetail.sub}`); + const { + data: user, + error, + loading, + } = useGetRequest(`/user/${userDetail.sub}`, { + initialDataState: { + user: {}, + }, + }); // const value = data.user; // console.log(data.user); - console.log(data); - return <>{/* */}; + useEffect(() => { + setProfileData(user); + }, [user]); + // console.log(profileData.user[0].files); + return ( + <> + {loading || error ? null : !Object.keys(profileData.user) + .length ? null : ( + + )} + + ); } diff --git a/package.json b/package.json index 657dfb8..f698b40 100644 --- a/package.json +++ b/package.json @@ -15,10 +15,12 @@ "formik": "^2.2.9", "next": "12.1.0", "react": "17.0.2", + "react-card-carousel": "^1.1.3", "react-chartjs-2": "^4.1.0", "react-dom": "17.0.2", "react-icons": "^4.3.1", "react-loader-spinner": "^6.0.0-0", + "react-multi-carousel": "^2.8.0", "react-phone-input-2": "^2.15.0", "uniqid": "^5.4.0", "yup": "^0.32.11" diff --git a/pages/dashboard/[type].js b/pages/dashboard/[type].js index 6bc9e74..f565ac8 100644 --- a/pages/dashboard/[type].js +++ b/pages/dashboard/[type].js @@ -1,6 +1,6 @@ import { useRouter } from "next/router"; import Head from "next/head"; -import { FaFileAlt, FaUser, FaCode } from "react-icons/fa"; +import { FaFileAlt, FaUser, FaChartBar, FaList } from "react-icons/fa"; import { DashboardSidebarButton } from "../../components/UI/buttons"; import Sidebar from "../../components/UI/Dashboard/Sidebar"; @@ -79,14 +79,14 @@ export default function DashboardPage() { linkTo={"/dashboard/analytics?tab=leetcode"} highlight={type === "analytics"} > - + Analytics - + Todo diff --git a/pages/index.js b/pages/index.js index 080232f..c69a2a5 100644 --- a/pages/index.js +++ b/pages/index.js @@ -1,6 +1,7 @@ import Head from "next/head"; import Image from "next/image"; import styles from "../styles/Home.module.css"; +import MyCarousel from "../components/UI/carousel"; export default function Home() { return ( @@ -15,40 +16,7 @@ export default function Home() {

    Welcome to CoderSpace!

    - -

    - Get started by creating your account{" "} - Hello World! -

    - -
    +
    ); diff --git a/public/images/analytics.png b/public/images/analytics.png new file mode 100644 index 0000000000000000000000000000000000000000..f879d9562d35dc22bcd4fa7bcb92c911926ea3d0 GIT binary patch literal 4067 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 literal 0 HcmV?d00001 diff --git a/public/images/file-upload.jpg b/public/images/file-upload.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e45d5b5f5e2591ef3d6c95951aa7babfb81fa2a6 GIT binary patch literal 71011 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 PXb6mk!0-wI#{V|~k7&s^ literal 0 HcmV?d00001 diff --git a/public/images/file.jpg b/public/images/file.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7112ac3167557d630b60b9a6ec3f6589a89feea7 GIT binary patch literal 3147 zcmb7Gc{J2*8~)8|EHjqujf@$REHUO? zNmWflLql3#*H8y8r zbbeCf@$G@jt+oCyV$7BCk14+og@*T(^Tg{f1MrQ=BOX7)?#$Kx3~TO_D?J3A~e(^to`wW-!L??x;6YVFU^;P?3@IhzEU!nI+F zk@UxhR*iD|yd0FXX{C1?kNqNXjLz&la!l)cf9~tY<-B6F;qyXQf7Joqu~3FDTO0Xd zV9L!Y?M-y6lkExE7Wg*^f}dEZ_R=_+aYiq4bp3nSl+iXoY>3?R3kOOa4yl>X@td1g zJv$XC<2$1exP|&b{MGuOfN+L_7XUy|&S%465ZK>yfr240I0)bp6tfW$7R9I%cZg_e zX_Kv71Z1#k2dp_GL2xDr;sdr$QmUHz#G9zHlPU*&4eo!o!d`W;?PvC_6;-bIyonrb zCl;~H?enqKHH+_17<#6eB|seQ~Sq&d)Es*3b$gp_#ON8rCg3bt5r(X83i}Wy}qV zTvppOBTHvGk>a2X$2Hf}+tpW&4#@M+aJmb@RrxGtdY_40F!@x|r8mc{aaM7Qc3Gw8 z^zuXUt_`br>gL=#uEGe*RhM$r+Y<#P zO4DrO3sh-%z`t9yuLC(!v7Cf@|1NWXhg{i8+_Wz1Vw2I~92u4`_gURex2};-MR&!a zNw~1`+6@1=C03BfZVK;3uVQdWtLH|gyJfeD2Ch^m)5^=l!BXwvXmCaQp_k`-0~U#4 zD(o}5Z-QnG{AUJJwb4oV^UY`YerhY12wj|&#f}iqWE)qsFjJU(`=2dVUe9UuYV1SZ zlq==6*F)betZc1P?)pkVg`D^4?7}D}Ioqu+f*9Ak zbKdi?RMqNoBIcTE)!6%J#cxcyy;Va<`tR+N`1%5tWrC;0iPn*?E!3L9ZJ=v5)} zaT^k;i02Tb`W~!>x4_K=?xbd4Wa4{j9LkZ)*f7ID!&D7hOL6HNHMaI9*;j_rUtA~&N?j+I#WNpG zEUINRqMjq~b6G4E93C^%mj6~BJELU9WOjEF8;*Y&?Z_b|4^txjj~jOTvW;8G&qBX% z15NB(3~gnJnH@FFLH<%QRNi&XRIN6S;b&BjNRH(z}0Gu^W^83yrGQk@5(?A*{M+NJ*U__S>^Rpl&p zEoE!rRrj=@0i!U9sO991DzYhkVX0~{fokCWSt3TV;`%Ftdi2@8op)qQT4Sy_)YhCM zU)Xt1eQlhQIIk$GIHeQl+IzF?p_okYg$5_Bf-M%4+*X@rS6uIew${1RHBd0bZ(JhO zuF%`4FjH5HFwCv)nUhrVemp;Vv^~}jQkD1AV=mpx@vb2`JKEf*BYZ*+7+fX!h=;X! z&Of0GuQqAYd9n)Hn*#W5ElhPjWYe`BZZ==Zgr?!hbW?*oDJEFo%vbC@=D4hVBfAXb zZ4u`3{WGH=*TYBnIt3wNk8ChS6xRxZXQ=AW^c9wbZ;PLmG*OOOc*0k8?kS!2DpozW z`RIxorm`B~N-Xq`HFe!ed3?57c0DnGf3~}Y=kYe6`T!iA$2SL&?eSNPgm?>}&9VQRlYjML+BUBK8v2oZCt-TN&TE@dAFZ zcB4?F@s^%Jr>|I5d)OEW6@)PnfFvluS&L{HUTekWsW?HtT$j zptnK(@y^fXt+j$b!(3LXzd=3bpat$eOoaE;o&?hV{_xT3xnU}I*}LqU1XLBOoBN-N z%)>%JWb=ofimV@@gICdmqvjs-iWyO;m8+J zqfhHr;pyx6@buKlcpkkeeHEfuvDy};Kd*iHgL2-c3yQ?VVdKIi38KfJTYL{snj-cMYIm@d{1q)>APedicnJ#bHesy zu072^O{}DQD}7XY?pNv_msAu1?w{1>9X7PNLFhQK$_#g8$+KcR4#by)-g&>fpJNlcr4OaqZpj%}uYy&?YUuAsS=x z+1_@FU)B)D&Ro-C!jq3G4}Hn1JDeEoVldJOe| zz2I-X$8L4V^ImdxuLrBX{KRCGBrpd7nOwUXAKNo*lZl$ND$BK)T+rnqb*rYk7wX^@DsKN{{=iJX>R}k literal 0 HcmV?d00001 diff --git a/public/images/todo.png b/public/images/todo.png new file mode 100644 index 0000000000000000000000000000000000000000..af7a6993a6d285861ab1caaa7b6abae8bb338b33 GIT binary patch literal 4059 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