From 280524245039a554aae77c7ab21ae94e698c79f4 Mon Sep 17 00:00:00 2001 From: calisio Date: Wed, 7 Aug 2024 15:14:59 -0500 Subject: [PATCH 1/2] fix: fixes all linting warnings --- README.md | 1 + frontend/src/Components/CatalogCourseCard.tsx | 108 ++++++------ .../src/Components/EnrolledCourseCard.tsx | 162 +++++++++--------- .../src/Components/MilestonesBarChart.tsx | 31 ++-- frontend/src/Components/ResourcesSideBar.tsx | 1 + frontend/src/Components/WeeklyActivity.tsx | 2 +- frontend/src/Pages/StudentDashboard.tsx | 5 +- 7 files changed, 152 insertions(+), 158 deletions(-) diff --git a/README.md b/README.md index 60ac4c9e..145cb5e7 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ If you would like to contribute, please have a look at our [contribution guideli - Clone the repository - `cp .env.example .env && cp frontend/.env.example frontend/.env` - run `make help` for complete instructions +- run `cd frontend && yarn prepare` **For frontend development:** diff --git a/frontend/src/Components/CatalogCourseCard.tsx b/frontend/src/Components/CatalogCourseCard.tsx index d2081ad7..7d9966db 100644 --- a/frontend/src/Components/CatalogCourseCard.tsx +++ b/frontend/src/Components/CatalogCourseCard.tsx @@ -90,65 +90,59 @@ export default function CatalogCourseCard({ return {pillLabel}; }); - return ( - <> - {view == ViewType.List ? ( - -
-
-
updateFavorite(e)}>{bookmark}
-

{course.program_name}

-

|

-

{course.provider_name}

- {programPill} - {outcomePills} -
-

- {course.description} -

+ if (view == ViewType.List) + return ( +
+
+
+
updateFavorite(e)}>{bookmark}
+

{course.program_name}

+

|

+

{course.provider_name}

+ {programPill} + {outcomePills}
-
- ) : ( -
-
updateFavorite(e)} - > - {bookmark} +

+ {course.description} +

+
+ + ); + return ( +
+
updateFavorite(e)} + > + {bookmark} +
+ +
+ {coverImage !== "" ? ( + + ) : ( +
+ )} +
+
- )} - + +
); } diff --git a/frontend/src/Components/EnrolledCourseCard.tsx b/frontend/src/Components/EnrolledCourseCard.tsx index fab68c8e..760e0999 100644 --- a/frontend/src/Components/EnrolledCourseCard.tsx +++ b/frontend/src/Components/EnrolledCourseCard.tsx @@ -43,95 +43,93 @@ export default function EnrolledCourseCard({ }); } - return ( - <> - {view == ViewType.List ? ( - -
-
updateFavorite(e)}> - {!recent && - (course.is_favorited ? ( - - ) : ( - - ))} -
-

{course.program_name}

-

|

-

{course.provider_platform_name}

-
- {status == CourseStatus.Completed ? ( -
- Course Completed -
- ) : status == CourseStatus.Pending ? ( -
- Course Pending -
- ) : ( -
- -
- )} -
- ) : ( -
-
updateFavorite(e)} - > + if (view == ViewType.List) + return ( + +
+
updateFavorite(e)}> {!recent && (course.is_favorited ? ( ) : ( - + ))}
-
-
- {coverImage !== "" ? ( - - ) : ( -
- )} -
-
-

- {course.provider_platform_name} -

-

- {course.alt_name && course.alt_name + " - "} - {course.program_name} -

-
- {status == CourseStatus.Completed ? ( -
- Course Completed -
- ) : status == CourseStatus.Pending ? ( -
- Course Pending -
- ) : ( - - )} +

{course.program_name}

+

|

+

{course.provider_platform_name}

+
+ {status == CourseStatus.Completed ? ( +
+ Course Completed +
+ ) : status == CourseStatus.Pending ? ( +
+ Course Pending +
+ ) : ( +
+ +
+ )} +
+ ); + return ( + - )} - + +
); } diff --git a/frontend/src/Components/MilestonesBarChart.tsx b/frontend/src/Components/MilestonesBarChart.tsx index 96987431..3bada081 100644 --- a/frontend/src/Components/MilestonesBarChart.tsx +++ b/frontend/src/Components/MilestonesBarChart.tsx @@ -20,25 +20,22 @@ const MilestonesBarChart = ({ data }: { data: any }) => { const maxYAxisLabel = (props) => { const { x, y, payload } = props; const name = payload.value; - return ( - <> - {name.length > 10 ? ( - <> - - {name.slice(0, 11)} - - - {name.length > 20 - ? name.slice(11, 20) + "..." - : name.slice(11, 20)} - - - ) : ( + if (name.length > 10) { + return ( + <> - {name} + {name.slice(0, 11)} + + + {name.length > 20 ? name.slice(11, 20) + "..." : name.slice(11, 20)} - )} - + + ); + } + return ( + + {name} + ); }; diff --git a/frontend/src/Components/ResourcesSideBar.tsx b/frontend/src/Components/ResourcesSideBar.tsx index 05c45c34..e95e8042 100644 --- a/frontend/src/Components/ResourcesSideBar.tsx +++ b/frontend/src/Components/ResourcesSideBar.tsx @@ -11,6 +11,7 @@ const ExternalLink = ({ children, url }: { children: any; url: string }) => { className="flex gap-2 body-small text-body-text items-center" href={url} target="_blank" + rel="noreferrer" > {children} diff --git a/frontend/src/Components/WeeklyActivity.tsx b/frontend/src/Components/WeeklyActivity.tsx index b19ae5a0..a899b8e6 100644 --- a/frontend/src/Components/WeeklyActivity.tsx +++ b/frontend/src/Components/WeeklyActivity.tsx @@ -17,7 +17,7 @@ const WeekActivityChart = ({ data }: { data: any }) => { var gridColor = theme == "light" ? "#ECECEC" : "#737373"; var backgroundColor = theme == "light" ? "#FFFFFF" : "#0F2926"; - const result: RecentActivity[] = new Array(7); + const result: RecentActivity[] = Array.from({ length: 7 }); let currentDate = new Date(); for (let i = 6; i >= 0; i--) { diff --git a/frontend/src/Pages/StudentDashboard.tsx b/frontend/src/Pages/StudentDashboard.tsx index 327e535f..a2563a9d 100644 --- a/frontend/src/Pages/StudentDashboard.tsx +++ b/frontend/src/Pages/StudentDashboard.tsx @@ -59,7 +59,10 @@ export default function StudentDashboard() {
    {data.top_programs.map((name: string) => { return ( -
  • +
  • {name}

  • From ee300e62c0ec91b418fa632d0892f6e374e87307 Mon Sep 17 00:00:00 2001 From: calisio Date: Wed, 7 Aug 2024 15:48:03 -0500 Subject: [PATCH 2/2] ci: adds prettier formatting file and formats all frontend files --- frontend/.eslintrc.cjs | 30 +- frontend/.eslintrc.json | 2 +- frontend/.prettierrc.json | 6 + frontend/README.md | 26 +- frontend/index.html | 18 +- frontend/package.json | 94 +-- frontend/postcss.config.cjs | 8 +- frontend/src/AuthContext.tsx | 126 +-- frontend/src/Components/ApplicationLogo.tsx | 4 +- frontend/src/Components/Brand.tsx | 20 +- frontend/src/Components/CatalogCourseCard.tsx | 262 ++++--- frontend/src/Components/CategoryItem.tsx | 250 +++--- frontend/src/Components/ConvertSeconds.tsx | 16 +- .../src/Components/CurrentlyEnrolledClass.tsx | 50 +- frontend/src/Components/DangerButton.tsx | 36 +- frontend/src/Components/DeleteForm.tsx | 54 +- .../src/Components/EnrolledCourseCard.tsx | 243 +++--- frontend/src/Components/LinkItem.tsx | 72 +- .../src/Components/MilestonesBarChart.tsx | 142 ++-- frontend/src/Components/Modal.tsx | 50 +- .../src/Components/MonthActivityChart.tsx | 102 +-- frontend/src/Components/NavLink.tsx | 42 +- frontend/src/Components/Navbar.tsx | 163 ++-- .../Components/NewOidcClientNotification.tsx | 74 +- frontend/src/Components/NotificationCard.tsx | 52 +- .../src/Components/NotificationsSideBar.tsx | 132 ++-- frontend/src/Components/PageNav.tsx | 251 +++--- frontend/src/Components/Pagination.tsx | 108 +-- frontend/src/Components/PrimaryButton.tsx | 36 +- frontend/src/Components/ProgressBar.tsx | 20 +- frontend/src/Components/ProviderCard.tsx | 170 ++-- frontend/src/Components/ResourcesSideBar.tsx | 190 ++--- frontend/src/Components/ResponsiveNavLink.tsx | 34 +- frontend/src/Components/SecondaryButton.tsx | 40 +- frontend/src/Components/StatsCard.tsx | 34 +- frontend/src/Components/ThemeContext.tsx | 56 +- frontend/src/Components/ThemeToggle.tsx | 38 +- frontend/src/Components/Toast.tsx | 76 +- frontend/src/Components/ToggleView.tsx | 54 +- .../Components/TopProgActivityPieChart.tsx | 65 +- frontend/src/Components/UserActivityMap.tsx | 619 +++++++-------- frontend/src/Components/WeeklyActivity.tsx | 187 ++--- .../src/Components/forms/AddCategoryForm.tsx | 66 +- frontend/src/Components/forms/AddLinkForm.tsx | 88 +-- .../src/Components/forms/AddProviderForm.tsx | 235 +++--- frontend/src/Components/forms/AddUserForm.tsx | 264 ++++--- .../Components/forms/ChangePasswordForm.tsx | 360 ++++----- .../forms/ConfirmImportAllUsersForm.tsx | 50 +- frontend/src/Components/forms/ConsentForm.tsx | 94 +-- frontend/src/Components/forms/DeleteForm.tsx | 52 +- .../src/Components/forms/EditProviderForm.tsx | 443 +++++------ .../src/Components/forms/EditUserForm.tsx | 202 ++--- frontend/src/Components/forms/LoginForm.tsx | 229 +++--- frontend/src/Components/forms/MapUserForm.tsx | 321 ++++---- .../forms/RegisterOidcClientForm.tsx | 164 ++-- .../Components/forms/ResetPasswordForm.tsx | 114 +-- .../Components/forms/ShowImportedUsers.tsx | 122 +-- .../Components/forms/ShowTempPasswordForm.tsx | 68 +- frontend/src/Components/inputs/Checkbox.tsx | 36 +- frontend/src/Components/inputs/CloseX.tsx | 20 +- .../src/Components/inputs/DropdownControl.tsx | 54 +- .../src/Components/inputs/DropdownInput.tsx | 76 +- frontend/src/Components/inputs/InputError.tsx | 24 +- frontend/src/Components/inputs/SearchBar.tsx | 40 +- .../src/Components/inputs/SubmitButton.tsx | 12 +- .../src/Components/inputs/TextAreaInput.tsx | 78 +- frontend/src/Components/inputs/TextInput.tsx | 96 +-- frontend/src/Components/inputs/index.js | 10 +- .../Components/pill-labels/DarkGreenPill.tsx | 6 +- .../src/Components/pill-labels/GreyPill.tsx | 8 +- .../Components/pill-labels/LightGreenPill.tsx | 8 +- .../src/Components/pill-labels/RedPill.tsx | 8 +- .../src/Components/pill-labels/TealPill.tsx | 8 +- .../src/Components/pill-labels/YellowPill.tsx | 8 +- frontend/src/Layouts/AuthenticatedLayout.tsx | 30 +- frontend/src/Layouts/GuestLayout.tsx | 28 +- frontend/src/Pages/AdminDashboard.tsx | 227 +++--- frontend/src/Pages/Auth/Consent.tsx | 12 +- frontend/src/Pages/Auth/Login.tsx | 44 +- frontend/src/Pages/Auth/ResetPassword.tsx | 16 +- frontend/src/Pages/CourseCatalog.tsx | 138 ++-- frontend/src/Pages/Dashboard.tsx | 34 +- frontend/src/Pages/Error.tsx | 44 +- frontend/src/Pages/MyCourses.tsx | 203 ++--- frontend/src/Pages/MyProgress.tsx | 279 ++++--- .../src/Pages/ProviderPlatformManagement.tsx | 489 ++++++------ frontend/src/Pages/ProviderUserManagement.tsx | 726 +++++++++--------- frontend/src/Pages/ResourcesManagement.tsx | 621 +++++++-------- frontend/src/Pages/StudentDashboard.tsx | 332 ++++---- frontend/src/Pages/UserActivity.tsx | 222 +++--- frontend/src/Pages/Users.tsx | 633 +++++++-------- frontend/src/Pages/Welcome.tsx | 457 +++++------ frontend/src/app.tsx | 190 ++--- frontend/src/bootstrap.ts | 4 +- frontend/src/common.ts | 238 +++--- frontend/src/css/app.css | 134 ++-- frontend/src/main.tsx | 36 +- frontend/src/types/global.d.ts | 14 +- frontend/src/types/index.d.ts | 28 +- frontend/tailwind.config.js | 190 ++--- frontend/tsconfig.json | 46 +- frontend/vite.config.ts | 44 +- 102 files changed, 6626 insertions(+), 6249 deletions(-) create mode 100644 frontend/.prettierrc.json diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs index 6e8698b7..9bc2cf82 100644 --- a/frontend/.eslintrc.cjs +++ b/frontend/.eslintrc.cjs @@ -1,18 +1,18 @@ module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:react-hooks/recommended", - ], - ignorePatterns: ["dist", ".eslintrc.cjs"], - parser: "@typescript-eslint/parser", - plugins: ["react-refresh"], - rules: { - "react-refresh/only-export-components": [ - "warn", - { allowConstantExport: true }, + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended' ], - }, + ignorePatterns: ['dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true } + ] + } }; diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json index bffb357a..1c2aa65d 100644 --- a/frontend/.eslintrc.json +++ b/frontend/.eslintrc.json @@ -1,3 +1,3 @@ { - "extends": "next/core-web-vitals" + "extends": "next/core-web-vitals" } diff --git a/frontend/.prettierrc.json b/frontend/.prettierrc.json new file mode 100644 index 00000000..1661de8f --- /dev/null +++ b/frontend/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "trailingComma": "none", + "tabWidth": 4, + "semi": true, + "singleQuote": true +} diff --git a/frontend/README.md b/frontend/README.md index bb156850..16d7e695 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -4,27 +4,27 @@ This template provides a minimal setup to get React working in Vite with HMR and Currently, two official plugins are available: -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh ## Expanding the ESLint configuration If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: -- Configure the top-level `parserOptions` property like this: +- Configure the top-level `parserOptions` property like this: ```js export default { - // other rules... - parserOptions: { - ecmaVersion: "latest", - sourceType: "module", - project: ["./tsconfig.json", "./tsconfig.node.json"], - tsconfigRootDir: __dirname, - }, + // other rules... + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: ['./tsconfig.json', './tsconfig.node.json'], + tsconfigRootDir: __dirname + } }; ``` -- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` -- Optionally add `plugin:@typescript-eslint/stylistic-type-checked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list +- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` +- Optionally add `plugin:@typescript-eslint/stylistic-type-checked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list diff --git a/frontend/index.html b/frontend/index.html index 37cc5b1b..3b5fa904 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,13 +1,13 @@ - - - UnlockEd - - + + + UnlockEd + + - -
    - - + +
    + + diff --git a/frontend/package.json b/frontend/package.json index 8c003508..cadf35dd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,49 +1,49 @@ { - "name": "unlockedv2", - "private": true, - "version": "0.0.0", - "type": "module", - "proxy": "http://localhost:8080", - "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "lint-staged": "oxlint src", - "preview": "vite preview", - "prepare": "cd .. && husky frontend/.husky" - }, - "dependencies": { - "autoprefixer": "^10.4.19", - "axios": "^1.6.8", - "daisyui": "^4.10.5", - "oxlint": "^0.3.2", - "postcss": "^8.4.38", - "prettier": "^3.2.5", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-hook-form": "^7.51.3", - "react-router-dom": "^6.23.0", - "recharts": "^2.12.7", - "swr": "^2.2.5", - "tailwindcss": "^3.4.3", - "usehooks-ts": "^3.1.0" - }, - "devDependencies": { - "@heroicons/react": "^2.0.18", - "@tailwindcss/forms": "^0.5.3", - "@tailwindcss/typography": "^0.5.10", - "@types/react": "^18.2.66", - "@types/react-dom": "^18.2.22", - "@typescript-eslint/eslint-plugin": "^7.2.0", - "@typescript-eslint/parser": "^7.2.0", - "@vitejs/plugin-react": "^4.2.1", - "eslint": "^8.57.0", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.6", - "husky": "^9.0.11", - "typescript": "^5.2.2", - "vite": "^5.2.0" - }, - "lint-staged": { - "**/*.{js,tsx,ts,css,md}": "prettier --write" - } + "name": "unlockedv2", + "private": true, + "version": "0.0.0", + "type": "module", + "proxy": "http://localhost:8080", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint-staged": "oxlint src", + "preview": "vite preview", + "prepare": "cd .. && husky frontend/.husky" + }, + "dependencies": { + "autoprefixer": "^10.4.19", + "axios": "^1.6.8", + "daisyui": "^4.10.5", + "oxlint": "^0.3.2", + "postcss": "^8.4.38", + "prettier": "^3.2.5", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-hook-form": "^7.51.3", + "react-router-dom": "^6.23.0", + "recharts": "^2.12.7", + "swr": "^2.2.5", + "tailwindcss": "^3.4.3", + "usehooks-ts": "^3.1.0" + }, + "devDependencies": { + "@heroicons/react": "^2.0.18", + "@tailwindcss/forms": "^0.5.3", + "@tailwindcss/typography": "^0.5.10", + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", + "@typescript-eslint/eslint-plugin": "^7.2.0", + "@typescript-eslint/parser": "^7.2.0", + "@vitejs/plugin-react": "^4.2.1", + "eslint": "^8.57.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.6", + "husky": "^9.0.11", + "typescript": "^5.2.2", + "vite": "^5.2.0" + }, + "lint-staged": { + "**/*.{js,tsx,ts,css,md}": "prettier --write" + } } diff --git a/frontend/postcss.config.cjs b/frontend/postcss.config.cjs index 12a703d9..fe66dd61 100644 --- a/frontend/postcss.config.cjs +++ b/frontend/postcss.config.cjs @@ -1,6 +1,6 @@ module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, + plugins: { + tailwindcss: {}, + autoprefixer: {} + } }; diff --git a/frontend/src/AuthContext.tsx b/frontend/src/AuthContext.tsx index 84bae1d4..737bcdf4 100644 --- a/frontend/src/AuthContext.tsx +++ b/frontend/src/AuthContext.tsx @@ -1,81 +1,81 @@ -import "@/bootstrap"; +import '@/bootstrap'; import React, { - createContext, - useContext, - useEffect, - useState, - Dispatch, - SetStateAction, -} from "react"; -import { User } from "./types"; -import axios from "axios"; + createContext, + useContext, + useEffect, + useState, + Dispatch, + SetStateAction +} from 'react'; +import { User } from './types'; +import axios from 'axios'; interface AuthContextType { - user: User | null; - setUser: Dispatch>; + user: User | null; + setUser: Dispatch>; } const AuthContext = createContext(null); export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ - children, + children }) => { - const [user, setUser] = useState(null); - const [loading, setLoading] = useState(true); - useEffect(() => { - const fetchUser = async () => { - try { - const response = await axios.get(`/api/auth`); - setUser(response.data); - } catch (error) { - console.log("Authentication check failed", error); - setUser(null); - } finally { - setLoading(false); - } - }; - fetchUser(); - }, []); + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + useEffect(() => { + const fetchUser = async () => { + try { + const response = await axios.get(`/api/auth`); + setUser(response.data); + } catch (error) { + console.log('Authentication check failed', error); + setUser(null); + } finally { + setLoading(false); + } + }; + fetchUser(); + }, []); - if (loading) { - return
    Loading...
    ; - } - if (!user) { - return null; - } else if ( - user.password_reset === true && - window.location.pathname !== "/reset-password" - ) { - window.location.href = "/reset-password"; - return null; - } - return ( - - {children} - - ); + if (loading) { + return
    Loading...
    ; + } + if (!user) { + return null; + } else if ( + user.password_reset === true && + window.location.pathname !== '/reset-password' + ) { + window.location.href = '/reset-password'; + return null; + } + return ( + + {children} + + ); }; export const useAuth = () => { - const context = useContext(AuthContext); - if (!context) { - throw new Error("useAuth must be used within an AuthProvider"); - } - return context; + const context = useContext(AuthContext); + if (!context) { + throw new Error('useAuth must be used within an AuthProvider'); + } + return context; }; export const handleLogout = async () => { - try { - await axios.post("/api/logout"); - const logout_response = await axios.get("/self-service/logout/browser"); - if (logout_response.data.logout_url) { - window.location.href = logout_response.data.logout_url; - } else { - console.log("Logout failed", logout_response); - window.location.href = "/self-service/login/browser"; + try { + await axios.post('/api/logout'); + const logout_response = await axios.get('/self-service/logout/browser'); + if (logout_response.data.logout_url) { + window.location.href = logout_response.data.logout_url; + } else { + console.log('Logout failed', logout_response); + window.location.href = '/self-service/login/browser'; + } + } catch (error) { + window.location.href = '/login'; + console.log('Logout failed', error); } - } catch (error) { - window.location.href = "/login"; - console.log("Logout failed", error); - } }; diff --git a/frontend/src/Components/ApplicationLogo.tsx b/frontend/src/Components/ApplicationLogo.tsx index 611f7577..bd11e595 100644 --- a/frontend/src/Components/ApplicationLogo.tsx +++ b/frontend/src/Components/ApplicationLogo.tsx @@ -1,5 +1,5 @@ -import { HTMLAttributes } from "react"; +import { HTMLAttributes } from 'react'; export default function ApplicationLogo(props: HTMLAttributes) { - return ; + return ; } diff --git a/frontend/src/Components/Brand.tsx b/frontend/src/Components/Brand.tsx index 0013a89a..f7b98684 100644 --- a/frontend/src/Components/Brand.tsx +++ b/frontend/src/Components/Brand.tsx @@ -1,12 +1,12 @@ export default function Brand() { - return ( -
    - -

    - Unlock - Ed - v2 -

    -
    - ); + return ( +
    + +

    + Unlock + Ed + v2 +

    +
    + ); } diff --git a/frontend/src/Components/CatalogCourseCard.tsx b/frontend/src/Components/CatalogCourseCard.tsx index 7d9966db..10053ccf 100644 --- a/frontend/src/Components/CatalogCourseCard.tsx +++ b/frontend/src/Components/CatalogCourseCard.tsx @@ -1,148 +1,158 @@ -import { BookmarkIcon } from "@heroicons/react/24/solid"; -import { BookmarkIcon as BookmarkIconOutline } from "@heroicons/react/24/outline"; -import LightGreenPill from "./pill-labels/LightGreenPill"; -import RedPill from "./pill-labels/RedPill"; -import YellowPill from "./pill-labels/YellowPill"; -import axios from "axios"; -import { ViewType } from "./ToggleView"; -import GreyPill from "./pill-labels/GreyPill"; -import { MouseEvent } from "react"; -import { CourseCatalogue } from "@/common"; +import { BookmarkIcon } from '@heroicons/react/24/solid'; +import { BookmarkIcon as BookmarkIconOutline } from '@heroicons/react/24/outline'; +import LightGreenPill from './pill-labels/LightGreenPill'; +import RedPill from './pill-labels/RedPill'; +import YellowPill from './pill-labels/YellowPill'; +import axios from 'axios'; +import { ViewType } from './ToggleView'; +import GreyPill from './pill-labels/GreyPill'; +import { MouseEvent } from 'react'; +import { CourseCatalogue } from '@/common'; export interface CatalogCourseCard { - name: string; - img_url: string; - url: string; - provider_platform_name: string; - tags: Array; - saved: boolean; + name: string; + img_url: string; + url: string; + provider_platform_name: string; + tags: Array; + saved: boolean; } export enum PillTagType { - Open = "open_enrollment", - Permission = "fixed_enrollment", - SelfPaced = "open_content", + Open = 'open_enrollment', + Permission = 'fixed_enrollment', + SelfPaced = 'open_content' } export enum OutcomePillType { - Certificate = "certificate", - CollegeCredit = "college_credit", + Certificate = 'certificate', + CollegeCredit = 'college_credit' } export default function CatalogCourseCard({ - course, - callMutate, - view, + course, + callMutate, + view }: { - course: CourseCatalogue; - callMutate: () => void; - view?: ViewType; + course: CourseCatalogue; + callMutate: () => void; + view?: ViewType; }) { - const coverImage = course.thumbnail_url; - const program_type: PillTagType = course.program_type as PillTagType; + const coverImage = course.thumbnail_url; + const program_type: PillTagType = course.program_type as PillTagType; - function updateFavorite(e: MouseEvent) { - e.preventDefault(); - axios - .put(`/api/programs/${course.program_id}/save`) - .then((response) => { - callMutate(); - console.log(response); - }) - .catch((error) => { - console.log(error); - }); - } + function updateFavorite(e: MouseEvent) { + e.preventDefault(); + axios + .put(`/api/programs/${course.program_id}/save`) + .then((response) => { + callMutate(); + console.log(response); + }) + .catch((error) => { + console.log(error); + }); + } - let programPill: JSX.Element; - if (program_type == PillTagType.Open) - programPill = Open Enrollment; - if (program_type == PillTagType.Permission) - programPill = Permission Only; - if (program_type == PillTagType.SelfPaced) - programPill = Self-Paced; + let programPill: JSX.Element; + if (program_type == PillTagType.Open) + programPill = Open Enrollment; + if (program_type == PillTagType.Permission) + programPill = Permission Only; + if (program_type == PillTagType.SelfPaced) + programPill = Self-Paced; - let bookmark: JSX.Element; - if (course.is_favorited) - bookmark = ; - else - bookmark = ( - - ); + let bookmark: JSX.Element; + if (course.is_favorited) + bookmark = ; + else + bookmark = ( + + ); - const outcomeTypes: OutcomePillType[] = course.outcome_types - .split(",") - .map((outcome) => outcome as OutcomePillType) - .filter((type) => - Object.values(OutcomePillType).includes(type as OutcomePillType), - ); - const outcomePills = outcomeTypes.map((outcomeString: string) => { - const outcome = outcomeString as OutcomePillType; - console.log(outcome); - const pillLabel = - outcome == OutcomePillType.Certificate - ? "Certificate Granting" - : outcome == OutcomePillType.CollegeCredit - ? "College Credit" - : "no label"; - return {pillLabel}; - }); + const outcomeTypes: OutcomePillType[] = course.outcome_types + .split(',') + .map((outcome) => outcome as OutcomePillType) + .filter((type) => + Object.values(OutcomePillType).includes(type as OutcomePillType) + ); + const outcomePills = outcomeTypes.map((outcomeString: string) => { + const outcome = outcomeString as OutcomePillType; + console.log(outcome); + const pillLabel = + outcome == OutcomePillType.Certificate + ? 'Certificate Granting' + : outcome == OutcomePillType.CollegeCredit + ? 'College Credit' + : 'no label'; + return ( + {pillLabel} + ); + }); - if (view == ViewType.List) + if (view == ViewType.List) + return ( + +
    +
    +
    updateFavorite(e)}>{bookmark}
    +

    {course.program_name}

    +

    |

    +

    {course.provider_name}

    + {programPill} + {outcomePills} +
    +

    + {course.description} +

    +
    +
    + ); return ( - -
    -
    -
    updateFavorite(e)}>{bookmark}
    -

    {course.program_name}

    -

    |

    -

    {course.provider_name}

    - {programPill} - {outcomePills} -
    -

    - {course.description} -

    +
    - ); - return ( - - ); } diff --git a/frontend/src/Components/CategoryItem.tsx b/frontend/src/Components/CategoryItem.tsx index 5de55814..2548ac75 100644 --- a/frontend/src/Components/CategoryItem.tsx +++ b/frontend/src/Components/CategoryItem.tsx @@ -1,127 +1,143 @@ -import { Category, CategoryLink } from "@/common"; -import { useRef, useState } from "react"; -import LinkItem from "./LinkItem"; -import AddLinkForm from "@/Components/forms/AddLinkForm"; +import { Category, CategoryLink } from '@/common'; +import { useRef, useState } from 'react'; +import LinkItem from './LinkItem'; +import AddLinkForm from '@/Components/forms/AddLinkForm'; import { - ChevronDownIcon, - ChevronRightIcon, - TrashIcon, - PlusIcon, - ChevronUpIcon, -} from "@heroicons/react/24/solid"; -import Modal, { ModalType } from "./Modal"; -import DeleteForm from "./forms/DeleteForm"; + ChevronDownIcon, + ChevronRightIcon, + TrashIcon, + PlusIcon, + ChevronUpIcon +} from '@heroicons/react/24/solid'; +import Modal, { ModalType } from './Modal'; +import DeleteForm from './forms/DeleteForm'; export default function CategoryItem({ - category, - deleteLink, - addLink, - moveLink, - updateLink, + category, + deleteLink, + addLink, + moveLink, + updateLink }: { - category: Category; - deleteLink: Function; - addLink: Function; - moveLink: Function; - updateLink: Function; + category: Category; + deleteLink: Function; + addLink: Function; + moveLink: Function; + updateLink: Function; }) { - const [activeLinkToDelete, setActiveLinkToDelete] = - useState(null); - const [open, setOpen] = useState(true); - const deleteLinkModal = useRef(null); - const addLinkModal = useRef(null); + const [activeLinkToDelete, setActiveLinkToDelete] = + useState(null); + const [open, setOpen] = useState(true); + const deleteLinkModal = useRef(null); + const addLinkModal = useRef(null); - return ( -
    - setOpen(!open)} - > -
    - {category.name} - {open ? ( - - ) : ( - - )} -
    -
      -
      -

      Title

      -

      URL

      -
      - {category.links.map((linkPair: CategoryLink, index) => { - const key = Object.keys(linkPair)[0]; - return ( -
      + setOpen(!open)} > - - updateLink(category, index, newLinkPair) +
      + {category.name} + {open ? ( + + ) : ( + + )} +
      +
        +
        +

        Title

        +

        URL

        +
        + {category.links.map((linkPair: CategoryLink, index) => { + const key = Object.keys(linkPair)[0]; + return ( +
        + + updateLink(category, index, newLinkPair) + } + /> +
        + { + setActiveLinkToDelete(linkPair), + deleteLinkModal.current?.showModal(); + }} + /> +
        +
        + + moveLink(category, index, 'up') + } + /> +
        +
        + + moveLink(category, index, 'down') + } + /> +
        +
        + ); + })} + +
      + {/* Modals */} + setActiveLinkToDelete(null)} + onSuccess={() => + deleteLink(category, activeLinkToDelete) + } + /> } - /> -
      - { - setActiveLinkToDelete(linkPair), - deleteLinkModal.current?.showModal(); - }} - /> -
      -
      - moveLink(category, index, "up")} - /> -
      -
      - moveLink(category, index, "down")} - /> -
      -
      - ); - })} - -
    - {/* Modals */} - setActiveLinkToDelete(null)} - onSuccess={() => deleteLink(category, activeLinkToDelete)} - /> - } - ref={deleteLinkModal} - /> - { - addLink(category, title, url), addLinkModal.current?.close(); - }} - /> - } - ref={addLinkModal} - /> -
    - ); + ref={deleteLinkModal} + /> + { + addLink(category, title, url), + addLinkModal.current?.close(); + }} + /> + } + ref={addLinkModal} + /> + + ); } diff --git a/frontend/src/Components/ConvertSeconds.tsx b/frontend/src/Components/ConvertSeconds.tsx index 732e13f3..bd861cfd 100644 --- a/frontend/src/Components/ConvertSeconds.tsx +++ b/frontend/src/Components/ConvertSeconds.tsx @@ -1,13 +1,13 @@ const convertSeconds = (secs: number) => { - const hours = Math.floor(secs / 3600); - const minutes = Math.floor((secs % 3600) / 60); - const seconds = Math.floor(secs % 60); + const hours = Math.floor(secs / 3600); + const minutes = Math.floor((secs % 3600) / 60); + const seconds = Math.floor(secs % 60); - return hours - ? { number: hours, label: `hr${hours === 1 ? "" : "s"}` } - : minutes - ? { number: minutes, label: `min${minutes === 1 ? "" : "s"}` } - : { number: seconds, label: `sec${seconds === 1 ? "" : "s"}` }; + return hours + ? { number: hours, label: `hr${hours === 1 ? '' : 's'}` } + : minutes + ? { number: minutes, label: `min${minutes === 1 ? '' : 's'}` } + : { number: seconds, label: `sec${seconds === 1 ? '' : 's'}` }; }; export default convertSeconds; diff --git a/frontend/src/Components/CurrentlyEnrolledClass.tsx b/frontend/src/Components/CurrentlyEnrolledClass.tsx index 74d50791..b5549229 100644 --- a/frontend/src/Components/CurrentlyEnrolledClass.tsx +++ b/frontend/src/Components/CurrentlyEnrolledClass.tsx @@ -1,26 +1,30 @@ -import { ArrowUpRightIcon } from "@heroicons/react/24/solid"; +import { ArrowUpRightIcon } from '@heroicons/react/24/solid'; export default function CurrentlyEnrolledClass({ course }: { course: any }) { - const truncateAltName = (altName: string) => { - if (altName.length < 15) return altName; - return altName.slice(0, 15) + "..."; - }; - return ( -
    -
    - {course.alt_name !== "" && ( -

    - {truncateAltName(course.alt_name)} -

    - )} -

    {course.name}

    -
    - - {course.provider_platform_name} - - -
    - ); + const truncateAltName = (altName: string) => { + if (altName.length < 15) return altName; + return altName.slice(0, 15) + '...'; + }; + return ( +
    +
    + {course.alt_name !== '' && ( +

    + {truncateAltName(course.alt_name)} +

    + )} +

    {course.name}

    +
    + + {course.provider_platform_name} + + +
    + ); } diff --git a/frontend/src/Components/DangerButton.tsx b/frontend/src/Components/DangerButton.tsx index 6524a0ad..8a234d97 100644 --- a/frontend/src/Components/DangerButton.tsx +++ b/frontend/src/Components/DangerButton.tsx @@ -1,22 +1,22 @@ -import { ButtonHTMLAttributes } from "react"; +import { ButtonHTMLAttributes } from 'react'; export default function DangerButton({ - className = "", - disabled, - children, - ...props + className = '', + disabled, + children, + ...props }: ButtonHTMLAttributes) { - return ( - - ); + return ( + + ); } diff --git a/frontend/src/Components/DeleteForm.tsx b/frontend/src/Components/DeleteForm.tsx index 71feabb6..f34b7cde 100644 --- a/frontend/src/Components/DeleteForm.tsx +++ b/frontend/src/Components/DeleteForm.tsx @@ -1,33 +1,33 @@ -import { CloseX } from "../Components/inputs/CloseX"; +import { CloseX } from '../Components/inputs/CloseX'; interface DeleteProps { - item: string; - onCancel: () => void; - onSuccess: () => void; + item: string; + onCancel: () => void; + onSuccess: () => void; } export default function DeleteForm({ item, onCancel, onSuccess }: DeleteProps) { - return ( -
    - onCancel()} /> -

    - Are you sure you would like to delete this {item.toLowerCase()}? -
    This action cannot be undone. -

    -

    -
    - - -
    -
    - ); + return ( +
    + onCancel()} /> +

    + Are you sure you would like to delete this {item.toLowerCase()}? +
    This action cannot be undone. +

    +

    +
    + + +
    +
    + ); } diff --git a/frontend/src/Components/EnrolledCourseCard.tsx b/frontend/src/Components/EnrolledCourseCard.tsx index 760e0999..65ae62e8 100644 --- a/frontend/src/Components/EnrolledCourseCard.tsx +++ b/frontend/src/Components/EnrolledCourseCard.tsx @@ -1,135 +1,140 @@ import { - CheckCircleIcon, - ClockIcon, - StarIcon, -} from "@heroicons/react/24/solid"; -import { StarIcon as StarIconOutline } from "@heroicons/react/24/outline"; -import ProgressBar from "./ProgressBar"; -import { CourseStatus } from "@/Pages/MyCourses"; -import { ViewType } from "./ToggleView"; -import axios from "axios"; + CheckCircleIcon, + ClockIcon, + StarIcon +} from '@heroicons/react/24/solid'; +import { StarIcon as StarIconOutline } from '@heroicons/react/24/outline'; +import ProgressBar from './ProgressBar'; +import { CourseStatus } from '@/Pages/MyCourses'; +import { ViewType } from './ToggleView'; +import axios from 'axios'; // this might also want to live within courses, as the type of course it is (ie currently enrolled, completed, favorited, pending) // recent would probably be a boolean, which would only need to be accessed on the homepage export interface CourseCard { - course: any; // TO DO: will change to be specific - recent?: boolean; - view?: ViewType; - callMutate?: () => void; + course: any; // TO DO: will change to be specific + recent?: boolean; + view?: ViewType; + callMutate?: () => void; } export default function EnrolledCourseCard({ - course, - recent, - view, - callMutate, + course, + recent, + view, + callMutate }: CourseCard) { - const coverImage = course.thumbnail_url; - let url = course.external_url; - let status: CourseStatus; - if (course.course_progress == 100) status = CourseStatus.Completed; + const coverImage = course.thumbnail_url; + let url = course.external_url; + let status: CourseStatus; + if (course.course_progress == 100) status = CourseStatus.Completed; - function updateFavorite(e) { - e.preventDefault(); - axios - .put(`/api/programs/${course.id}/save`) - .then((response) => { - callMutate(); - console.log(response); - }) - .catch((error) => { - console.log(error); - }); - } + function updateFavorite(e) { + e.preventDefault(); + axios + .put(`/api/programs/${course.id}/save`) + .then((response) => { + callMutate(); + console.log(response); + }) + .catch((error) => { + console.log(error); + }); + } - if (view == ViewType.List) + if (view == ViewType.List) + return ( + +
    +
    updateFavorite(e)}> + {!recent && + (course.is_favorited ? ( + + ) : ( + + ))} +
    +

    {course.program_name}

    +

    |

    +

    {course.provider_platform_name}

    +
    + {status == CourseStatus.Completed ? ( +
    + Course Completed +
    + ) : status == CourseStatus.Pending ? ( +
    + Course Pending +
    + ) : ( +
    + +
    + )} +
    + ); return ( - -
    -
    updateFavorite(e)}> - {!recent && - (course.is_favorited ? ( - - ) : ( - - ))} -
    -

    {course.program_name}

    -

    |

    -

    {course.provider_platform_name}

    +
    - {status == CourseStatus.Completed ? ( -
    - Course Completed -
    - ) : status == CourseStatus.Pending ? ( -
    - Course Pending -
    - ) : ( -
    - -
    - )} - ); - return ( - - ); } diff --git a/frontend/src/Components/LinkItem.tsx b/frontend/src/Components/LinkItem.tsx index c007355b..28c10dc4 100644 --- a/frontend/src/Components/LinkItem.tsx +++ b/frontend/src/Components/LinkItem.tsx @@ -1,42 +1,42 @@ -import { CategoryLink } from "@/common"; -import { useState } from "react"; +import { CategoryLink } from '@/common'; +import { useState } from 'react'; export default function LinkItem({ - linkName, - linkURL, - callUpdateLink, + linkName, + linkURL, + callUpdateLink }: { - linkName: string; - linkURL: string; - callUpdateLink: any; + linkName: string; + linkURL: string; + callUpdateLink: any; }) { - const [name, setName] = useState(linkName); - const [url, setURL] = useState(linkURL); + const [name, setName] = useState(linkName); + const [url, setURL] = useState(linkURL); - return ( -
  • - setName(e.target.value)} - onBlur={() => { - const newLinkPair: CategoryLink = {}; - newLinkPair[name] = url; - callUpdateLink(newLinkPair); - }} - className="input input-bordered w-1/3" - /> - setURL(e.target.value)} - onBlur={() => { - const newLinkPair: CategoryLink = {}; - newLinkPair[name] = url; - callUpdateLink(newLinkPair); - }} - className="input input-bordered w-2/3" - /> -
  • - ); + return ( +
  • + setName(e.target.value)} + onBlur={() => { + const newLinkPair: CategoryLink = {}; + newLinkPair[name] = url; + callUpdateLink(newLinkPair); + }} + className="input input-bordered w-1/3" + /> + setURL(e.target.value)} + onBlur={() => { + const newLinkPair: CategoryLink = {}; + newLinkPair[name] = url; + callUpdateLink(newLinkPair); + }} + className="input input-bordered w-2/3" + /> +
  • + ); } diff --git a/frontend/src/Components/MilestonesBarChart.tsx b/frontend/src/Components/MilestonesBarChart.tsx index 3bada081..118762dd 100644 --- a/frontend/src/Components/MilestonesBarChart.tsx +++ b/frontend/src/Components/MilestonesBarChart.tsx @@ -1,74 +1,88 @@ import { - BarChart, - Bar, - XAxis, - YAxis, - Tooltip, - ResponsiveContainer, - CartesianGrid, - Label, -} from "recharts"; -import { ThemeContext } from "./ThemeContext"; -import { useContext } from "react"; + BarChart, + Bar, + XAxis, + YAxis, + Tooltip, + ResponsiveContainer, + CartesianGrid, + Label +} from 'recharts'; +import { ThemeContext } from './ThemeContext'; +import { useContext } from 'react'; const MilestonesBarChart = ({ data }: { data: any }) => { - const { theme } = useContext(ThemeContext); + const { theme } = useContext(ThemeContext); - var barColor = theme == "light" ? "#18ABA0" : "#61BAB2"; - var backgroundColor = theme == "light" ? "#FFFFFF" : "#0F2926"; + var barColor = theme == 'light' ? '#18ABA0' : '#61BAB2'; + var backgroundColor = theme == 'light' ? '#FFFFFF' : '#0F2926'; - const maxYAxisLabel = (props) => { - const { x, y, payload } = props; - const name = payload.value; - if (name.length > 10) { - return ( - <> - - {name.slice(0, 11)} - - - {name.length > 20 ? name.slice(11, 20) + "..." : name.slice(11, 20)} - - - ); - } - return ( - - {name} - - ); - }; + const maxYAxisLabel = (props) => { + const { x, y, payload } = props; + const name = payload.value; + if (name.length > 10) { + return ( + <> + + {name.slice(0, 11)} + + + {name.length > 20 + ? name.slice(11, 20) + '...' + : name.slice(11, 20)} + + + ); + } + return ( + + {name} + + ); + }; - const YAxisTick = (props) => { - return {maxYAxisLabel(props)}; - }; + const YAxisTick = (props) => { + return {maxYAxisLabel(props)}; + }; - return ( - - - - - - } - /> - - - - - ); + return ( + + + + + + } + /> + + + + + ); }; export default MilestonesBarChart; diff --git a/frontend/src/Components/Modal.tsx b/frontend/src/Components/Modal.tsx index 1b7f5bf1..93a02ed6 100644 --- a/frontend/src/Components/Modal.tsx +++ b/frontend/src/Components/Modal.tsx @@ -1,37 +1,37 @@ -import { ReactNode, forwardRef } from "react"; +import { ReactNode, forwardRef } from 'react'; export interface ModalProps { - type: ModalType | string; - item: string; - form: ReactNode | null; + type: ModalType | string; + item: string; + form: ReactNode | null; } export enum ModalType { - Edit = "Edit", - Add = "Add", - Show = "Show", - Associate = "Associate", - Confirm = "Confirm", - Register = "Register", - Blank = "", + Edit = 'Edit', + Add = 'Add', + Show = 'Show', + Associate = 'Associate', + Confirm = 'Confirm', + Register = 'Register', + Blank = '' } const Modal = forwardRef(function Modal( - { type, item, form }, - ref, + { type, item, form }, + ref ) { - return ( - -
    -
    - - {type} {item} - -
    {form}
    -
    -
    -
    - ); + return ( + +
    +
    + + {type} {item} + +
    {form}
    +
    +
    +
    + ); }); export default Modal; diff --git a/frontend/src/Components/MonthActivityChart.tsx b/frontend/src/Components/MonthActivityChart.tsx index 1137de91..54850046 100644 --- a/frontend/src/Components/MonthActivityChart.tsx +++ b/frontend/src/Components/MonthActivityChart.tsx @@ -1,59 +1,59 @@ import { - LineChart, - Line, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - ResponsiveContainer, -} from "recharts"; -import { ThemeContext } from "./ThemeContext"; -import { useContext } from "react"; + LineChart, + Line, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + ResponsiveContainer +} from 'recharts'; +import { ThemeContext } from './ThemeContext'; +import { useContext } from 'react'; const ActivityChart = ({ data }: { data: any }) => { - const { theme } = useContext(ThemeContext); + const { theme } = useContext(ThemeContext); - var lineColor = theme == "light" ? "#18ABA0" : "#61BAB2"; - var gridColor = theme == "light" ? "#ECECEC" : "#737373"; - var backgroundColor = theme == "light" ? "#FFFFFF" : "#0F2926"; + var lineColor = theme == 'light' ? '#18ABA0' : '#61BAB2'; + var gridColor = theme == 'light' ? '#ECECEC' : '#737373'; + var backgroundColor = theme == 'light' ? '#FFFFFF' : '#0F2926'; - return ( - - - - - - - - - - ); + return ( + + + + + + + + + + ); }; export default ActivityChart; diff --git a/frontend/src/Components/NavLink.tsx b/frontend/src/Components/NavLink.tsx index f3a336ad..12e755a4 100644 --- a/frontend/src/Components/NavLink.tsx +++ b/frontend/src/Components/NavLink.tsx @@ -1,27 +1,27 @@ export interface NavLinkProps { - href: string; - className?: string; - children: React.ReactNode; + href: string; + className?: string; + children: React.ReactNode; } export default function NavLink({ - active = false, - className = "", - children, - ...props + active = false, + className = '', + children, + ...props }: NavLinkProps & { active: boolean }) { - return ( -
    - {children} -
    - ); + return ( +
    + {children} +
    + ); } diff --git a/frontend/src/Components/Navbar.tsx b/frontend/src/Components/Navbar.tsx index 045cc377..ea498e54 100644 --- a/frontend/src/Components/Navbar.tsx +++ b/frontend/src/Components/Navbar.tsx @@ -1,85 +1,86 @@ -import { UserRole } from "@/common"; -import Brand from "./Brand"; +import { UserRole } from '@/common'; +import Brand from './Brand'; import { - ArchiveBoxIcon, - BookOpenIcon, - BuildingStorefrontIcon, - ChartBarIcon, - HomeIcon, - RectangleStackIcon, - TrophyIcon, - UsersIcon, -} from "@heroicons/react/24/solid"; -import { useAuth } from "@/AuthContext"; + ArchiveBoxIcon, + BookOpenIcon, + BuildingStorefrontIcon, + ChartBarIcon, + HomeIcon, + RectangleStackIcon, + TrophyIcon, + UsersIcon +} from '@heroicons/react/24/solid'; +import { useAuth } from '@/AuthContext'; export default function Navbar() { - const user = useAuth(); - return ( -
    - - {/*
    + const user = useAuth(); + return ( +
    + + {/*
    */} -
    - ); +
    + ); } diff --git a/frontend/src/Components/NewOidcClientNotification.tsx b/frontend/src/Components/NewOidcClientNotification.tsx index 29436754..faca7c6a 100644 --- a/frontend/src/Components/NewOidcClientNotification.tsx +++ b/frontend/src/Components/NewOidcClientNotification.tsx @@ -1,42 +1,44 @@ -import { CloseX } from "./inputs/CloseX"; -import { OidcClient } from "@/common"; +import { CloseX } from './inputs/CloseX'; +import { OidcClient } from '@/common'; export default function NewOidcClientNotification({ - client, - onClose, + client, + onClose }: { - client: OidcClient; - onClose: () => void; + client: OidcClient; + onClose: () => void; }) { - return ( -
    -
    -

    - OIDC Client Registration Successful -

    - onClose()} /> -
    -
    -
    - Please make sure to save the following information. It will not be - displayed again. + return ( +
    +
    +

    + OIDC Client Registration Successful +

    + onClose()} /> +
    +
    +
    + Please make sure to save the following information. It will + not be displayed again. +
    +
    +
    + Client ID: +
    +
    {client.client_id}
    +
    +

    Client Secret:

    +
    {client.client_secret}
    +
    +

    Authorization Endpoint:

    +
    {client.auth_url}
    +
    +

    Token Endpoint:

    +
    {client.token_url}
    +
    +

    Scopes:

    +
    {client.scopes}
    +
    -
    -
    Client ID:
    -
    {client.client_id}
    -
    -

    Client Secret:

    -
    {client.client_secret}
    -
    -

    Authorization Endpoint:

    -
    {client.auth_url}
    -
    -

    Token Endpoint:

    -
    {client.token_url}
    -
    -

    Scopes:

    -
    {client.scopes}
    -
    -
    - ); + ); } diff --git a/frontend/src/Components/NotificationCard.tsx b/frontend/src/Components/NotificationCard.tsx index cc7c99b7..dab5377e 100644 --- a/frontend/src/Components/NotificationCard.tsx +++ b/frontend/src/Components/NotificationCard.tsx @@ -1,31 +1,35 @@ export enum NotificationType { - Announcement = "Announcement", - ToDo = "ToDo", + Announcement = 'Announcement', + ToDo = 'ToDo' } // TO DO: specify cardInfo type export default function NotificationCard({ - cardInfo, - type, + cardInfo, + type }: { - cardInfo: any; - type: NotificationType; + cardInfo: any; + type: NotificationType; }) { - return ( - -

    - {cardInfo.course_name && cardInfo.course_name + ": "} - {cardInfo.title} -

    - {type == NotificationType.Announcement ? ( -

    {cardInfo.message}

    - ) : ( - <> -

    - {cardInfo.course_name + " | " + cardInfo.provider_platform} -

    -

    {cardInfo.due.toUTCString()}

    - - )} -
    - ); + return ( + +

    + {cardInfo.course_name && cardInfo.course_name + ': '} + {cardInfo.title} +

    + {type == NotificationType.Announcement ? ( +

    {cardInfo.message}

    + ) : ( + <> +

    + {cardInfo.course_name + + ' | ' + + cardInfo.provider_platform} +

    +

    + {cardInfo.due.toUTCString()} +

    + + )} +
    + ); } diff --git a/frontend/src/Components/NotificationsSideBar.tsx b/frontend/src/Components/NotificationsSideBar.tsx index e47531d3..6c9e0473 100644 --- a/frontend/src/Components/NotificationsSideBar.tsx +++ b/frontend/src/Components/NotificationsSideBar.tsx @@ -1,79 +1,79 @@ -import NotificationCard, { NotificationType } from "./NotificationCard"; +import NotificationCard, { NotificationType } from './NotificationCard'; const date = new Date(); const announcements = [ - { - title: "Enrollment Approved", - message: - "You are now enrolled in Data and Algorithms from Harvard University.", - url: "", - }, - { - course_name: "Advanced English Composition", - title: "Schedule Change", - message: "Advanced English Composition is now M/W 10:30am-12pm.", - url: "", - }, - { - course_name: "Linear Algebra", - title: "Midterm Grades Out", - message: - "Hi everyone, midterm grades have been posted. Please reach out if you have any questions.", - url: "", - }, + { + title: 'Enrollment Approved', + message: + 'You are now enrolled in Data and Algorithms from Harvard University.', + url: '' + }, + { + course_name: 'Advanced English Composition', + title: 'Schedule Change', + message: 'Advanced English Composition is now M/W 10:30am-12pm.', + url: '' + }, + { + course_name: 'Linear Algebra', + title: 'Midterm Grades Out', + message: + 'Hi everyone, midterm grades have been posted. Please reach out if you have any questions.', + url: '' + } ]; const toDo = [ - { - title: "Assignment 4", - course_name: "Introduction to Computer Science", - provider_platform: "Kolibri", - url: "", - due: date, - }, - { - title: "Homework 6", - course_name: "Linear Algebra", - provider_platform: "WashU Canvas", - url: "", - due: date, - }, + { + title: 'Assignment 4', + course_name: 'Introduction to Computer Science', + provider_platform: 'Kolibri', + url: '', + due: date + }, + { + title: 'Homework 6', + course_name: 'Linear Algebra', + provider_platform: 'WashU Canvas', + url: '', + due: date + } ]; export default function NotificationsSideBar() { - // call the data here in the future + // call the data here in the future - return ( -
    -
    -

    Announcements

    -
    - {announcements.map((cardInfo: any) => { - return ( - - ); - })} + return ( +
    +
    +

    Announcements

    +
    + {announcements.map((cardInfo: any) => { + return ( + + ); + })} +
    +
    +
    +

    To Do

    +
    + {toDo.map((cardInfo: any) => { + return ( + + ); + })} +
    +
    -
    -
    -

    To Do

    -
    - {toDo.map((cardInfo: any) => { - return ( - - ); - })} -
    -
    -
    - ); + ); } diff --git a/frontend/src/Components/PageNav.tsx b/frontend/src/Components/PageNav.tsx index 8d99df33..f9b64905 100644 --- a/frontend/src/Components/PageNav.tsx +++ b/frontend/src/Components/PageNav.tsx @@ -1,137 +1,142 @@ -import { User, UserRole } from "../common"; -import { useEffect, useRef } from "react"; +import { User, UserRole } from '../common'; +import { useEffect, useRef } from 'react'; import { - ArrowRightEndOnRectangleIcon, - HomeIcon, - UsersIcon, - ChartBarIcon, - RectangleStackIcon, - ArchiveBoxIcon, -} from "@heroicons/react/24/solid"; -import ThemeToggle from "./ThemeToggle"; -import { handleLogout } from "../AuthContext"; + ArrowRightEndOnRectangleIcon, + HomeIcon, + UsersIcon, + ChartBarIcon, + RectangleStackIcon, + ArchiveBoxIcon +} from '@heroicons/react/24/solid'; +import ThemeToggle from './ThemeToggle'; +import { handleLogout } from '../AuthContext'; export default function PageNav({ - user, - path, + user, + path }: { - user: User; - path: Array; + user: User; + path: Array; }) { - const detailsRef = useRef(null); - useEffect(() => { - const closeDropdown = ({ target }: MouseEvent) => { - if (detailsRef.current && !detailsRef.current?.contains(target as Node)) { - detailsRef.current.removeAttribute("open"); - } - }; + const detailsRef = useRef(null); + useEffect(() => { + const closeDropdown = ({ target }: MouseEvent) => { + if ( + detailsRef.current && + !detailsRef.current?.contains(target as Node) + ) { + detailsRef.current.removeAttribute('open'); + } + }; - window.addEventListener("click", closeDropdown); + window.addEventListener('click', closeDropdown); - return () => { - window.removeEventListener("click", closeDropdown); - }; - }, []); + return () => { + window.removeEventListener('click', closeDropdown); + }; + }, []); - return ( -
    -
    -
      -
    • - -
    • - {path.map((p) => ( -
    • {p}
    • - ))} -
    -
    -
    -
      -
    • -
      - - - {user.name_first} {user.name_last} - - - +
      +
    • +
    +
    +
    + ); } diff --git a/frontend/src/Components/Pagination.tsx b/frontend/src/Components/Pagination.tsx index 7c7cc452..9a1c1a09 100644 --- a/frontend/src/Components/Pagination.tsx +++ b/frontend/src/Components/Pagination.tsx @@ -1,63 +1,63 @@ -import { PaginationMeta } from "@/common"; +import { PaginationMeta } from '@/common'; import { - ChevronDoubleLeftIcon, - ChevronDoubleRightIcon, -} from "@heroicons/react/24/solid"; + ChevronDoubleLeftIcon, + ChevronDoubleRightIcon +} from '@heroicons/react/24/solid'; export default function Pagination({ - meta, - setPage, + meta, + setPage }: { - meta: PaginationMeta; - setPage: (page: number) => void; + meta: PaginationMeta; + setPage: (page: number) => void; }) { - const page = meta.current_page - 1; + const page = meta.current_page - 1; - return ( -
    -
    0 ? "tooltip tooltip-left" : ""}`} - data-tip="First Page" - > - -
    - - {[page - 2, page - 1, page, page + 1, page + 2] - .filter((i) => i >= 0 && i < meta.last_page) - .map((i) => { - return ( - - ); - })} + +
    + + {[page - 2, page - 1, page, page + 1, page + 2] + .filter((i) => i >= 0 && i < meta.last_page) + .map((i) => { + return ( + + ); + })} -
    - -
    -
    - ); +
    + +
    +
    + ); } diff --git a/frontend/src/Components/PrimaryButton.tsx b/frontend/src/Components/PrimaryButton.tsx index 8ed21901..53c2a945 100644 --- a/frontend/src/Components/PrimaryButton.tsx +++ b/frontend/src/Components/PrimaryButton.tsx @@ -1,22 +1,22 @@ -import { ButtonHTMLAttributes } from "react"; +import { ButtonHTMLAttributes } from 'react'; export default function PrimaryButton({ - className = "", - disabled, - children, - ...props + className = '', + disabled, + children, + ...props }: ButtonHTMLAttributes) { - return ( - - ); + return ( + + ); } diff --git a/frontend/src/Components/ProgressBar.tsx b/frontend/src/Components/ProgressBar.tsx index 17e3434c..1bcb1d5b 100644 --- a/frontend/src/Components/ProgressBar.tsx +++ b/frontend/src/Components/ProgressBar.tsx @@ -1,12 +1,12 @@ export default function ProgressBar({ percent }: { percent: number }) { - return ( -
    - -
    {percent} %
    -
    - ); + return ( +
    + +
    {percent} %
    +
    + ); } diff --git a/frontend/src/Components/ProviderCard.tsx b/frontend/src/Components/ProviderCard.tsx index 9c8da48d..c10c3309 100644 --- a/frontend/src/Components/ProviderCard.tsx +++ b/frontend/src/Components/ProviderCard.tsx @@ -1,85 +1,97 @@ -import { useNavigate } from "react-router-dom"; -import { ProviderPlatform, ProviderPlatformState } from "@/common"; +import { useNavigate } from 'react-router-dom'; +import { ProviderPlatform, ProviderPlatformState } from '@/common'; import { - PencilSquareIcon, - CheckCircleIcon, - LinkIcon, - UserGroupIcon, - InformationCircleIcon, -} from "@heroicons/react/24/outline"; -import TealPill from "./pill-labels/TealPill"; -import YellowPill from "./pill-labels/YellowPill"; -import OutcomePill from "./pill-labels/GreyPill"; + PencilSquareIcon, + CheckCircleIcon, + LinkIcon, + UserGroupIcon, + InformationCircleIcon +} from '@heroicons/react/24/outline'; +import TealPill from './pill-labels/TealPill'; +import YellowPill from './pill-labels/YellowPill'; +import OutcomePill from './pill-labels/GreyPill'; export default function ProviderCard({ - provider, - openEditProvider, - oidcClient, - showAuthorizationInfo, + provider, + openEditProvider, + oidcClient, + showAuthorizationInfo }: { - provider: ProviderPlatform; - openEditProvider: Function; - oidcClient: Function; - showAuthorizationInfo: Function; + provider: ProviderPlatform; + openEditProvider: Function; + oidcClient: Function; + showAuthorizationInfo: Function; }) { - const navigate = useNavigate(); - return ( - - {provider.name} - - {provider.oidc_id !== 0 ? ( - - ) : ( -
    - )} - - - {/* TO DO: FINISH THIS */} - {provider.state == ProviderPlatformState.ENABLED ? ( - enabled - ) : provider.state == ProviderPlatformState.DISABLED ? ( - disabled - ) : provider.state == ProviderPlatformState.ARCHIVED ? ( - archived - ) : ( -

    Status unavailable

    - )} - - - {provider.state !== ProviderPlatformState.ARCHIVED && ( - <> - {provider.oidc_id !== 0 ? ( - <> -
    - showAuthorizationInfo(provider)} - /> -
    -
    - navigate(`/provider-users/${provider.id}`)} - /> -
    - - ) : ( -
    - oidcClient(provider)} - /> -
    - )} -
    - openEditProvider(provider)} - /> -
    - - )} - - - ); + const navigate = useNavigate(); + return ( + + {provider.name} + + {provider.oidc_id !== 0 ? ( + + ) : ( +
    + )} + + + {/* TO DO: FINISH THIS */} + {provider.state == ProviderPlatformState.ENABLED ? ( + enabled + ) : provider.state == ProviderPlatformState.DISABLED ? ( + disabled + ) : provider.state == ProviderPlatformState.ARCHIVED ? ( + archived + ) : ( +

    Status unavailable

    + )} + + + {provider.state !== ProviderPlatformState.ARCHIVED && ( + <> + {provider.oidc_id !== 0 ? ( + <> +
    + + showAuthorizationInfo(provider) + } + /> +
    +
    + + navigate( + `/provider-users/${provider.id}` + ) + } + /> +
    + + ) : ( +
    + oidcClient(provider)} + /> +
    + )} +
    + openEditProvider(provider)} + /> +
    + + )} + + + ); } diff --git a/frontend/src/Components/ResourcesSideBar.tsx b/frontend/src/Components/ResourcesSideBar.tsx index e95e8042..1f7f071b 100644 --- a/frontend/src/Components/ResourcesSideBar.tsx +++ b/frontend/src/Components/ResourcesSideBar.tsx @@ -1,114 +1,114 @@ -import { Category, Link, Resource, ServerResponse } from "@/common"; -import Error from "@/Pages/Error"; -import useSWR from "swr"; -import KolibriImg from "../../public/kolibri-card-cover.png"; -import WikiImg from "../../public/wikipedia.png"; -import { ArrowTopRightOnSquareIcon } from "@heroicons/react/24/outline"; +import { Category, Link, Resource, ServerResponse } from '@/common'; +import Error from '@/Pages/Error'; +import useSWR from 'swr'; +import KolibriImg from '../../public/kolibri-card-cover.png'; +import WikiImg from '../../public/wikipedia.png'; +import { ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline'; const ExternalLink = ({ children, url }: { children: any; url: string }) => { - return ( - - - {children} - - ); + return ( + + + {children} + + ); }; // this isnt best practice but this is just a temp solution so will change in future & creat a component if need be const KolibriCard = () => { - return ( -
    - Kolibri logo -
    -

    Kolibri

    -

    - Kolibri provides an extensive library of educational content suitable - for all learning levels. -

    - {/* Temporary-- replace with external link to Kolibri */} - - Explore Kolibri's Content - -
    -
    - ); + return ( +
    + Kolibri logo +
    +

    Kolibri

    +

    + Kolibri provides an extensive library of educational content + suitable for all learning levels. +

    + {/* Temporary-- replace with external link to Kolibri */} + + Explore Kolibri's Content + +
    +
    + ); }; const WikiCard = () => { - return ( -
    -
    - Wikipedia logo -
    -
    -

    Wikipedia

    -

    - Wikipedia offers a vast collection of articles covering a wide range - of topics across various academic disciplines. -

    - {/* Temporary-- replace with external link to Wiki */} - - Explore Wikipedia's Content - -
    -
    - ); + return ( +
    +
    + Wikipedia logo +
    +
    +

    Wikipedia

    +

    + Wikipedia offers a vast collection of articles covering a + wide range of topics across various academic disciplines. +

    + {/* Temporary-- replace with external link to Wiki */} + + Explore Wikipedia's Content + +
    +
    + ); }; const ResourcesCard = ({ resource }: { resource: Resource }) => { - return ( -
    -
    -

    {resource.name}

    - {resource.links.map((link: Link) => { - const [title, url] = Object.entries(link)[0]; - return ( - - {title} - - ); - })} -
    -
    - ); + return ( +
    +
    +

    {resource.name}

    + {resource.links.map((link: Link) => { + const [title, url] = Object.entries(link)[0]; + return ( + + {title} + + ); + })} +
    +
    + ); }; export default function ResourcesSideBar() { - const { data, isLoading, error } = - useSWR>("/api/left-menu"); + const { data, isLoading, error } = + useSWR>('/api/left-menu'); - if (isLoading) return
    Loading...
    ; - if (error) return ; + if (isLoading) return
    Loading...
    ; + if (error) return ; - return ( -
    -
    -

    Open Content

    - - -
    -
    -

    Resources

    -
    - {data.data.map((category: Category, index: number) => { - return ( - - ); - })} + return ( +
    +
    +

    Open Content

    + + +
    +
    +

    Resources

    +
    + {data.data.map((category: Category, index: number) => { + return ( + + ); + })} +
    +
    -
    -
    - ); + ); } diff --git a/frontend/src/Components/ResponsiveNavLink.tsx b/frontend/src/Components/ResponsiveNavLink.tsx index 3edee0b2..19bd7266 100644 --- a/frontend/src/Components/ResponsiveNavLink.tsx +++ b/frontend/src/Components/ResponsiveNavLink.tsx @@ -1,21 +1,21 @@ -import { NavLinkProps } from "./NavLink"; +import { NavLinkProps } from './NavLink'; export default function ResponsiveNavLink({ - active = false, - className = "", - children, - ...props + active = false, + className = '', + children, + ...props }: NavLinkProps & { active?: boolean }) { - return ( -
    - {children} -
    - ); + return ( +
    + {children} +
    + ); } diff --git a/frontend/src/Components/SecondaryButton.tsx b/frontend/src/Components/SecondaryButton.tsx index c88d142d..9f8b2478 100644 --- a/frontend/src/Components/SecondaryButton.tsx +++ b/frontend/src/Components/SecondaryButton.tsx @@ -1,24 +1,24 @@ -import { ButtonHTMLAttributes } from "react"; +import { ButtonHTMLAttributes } from 'react'; export default function SecondaryButton({ - type = "button", - className = "", - disabled, - children, - ...props + type = 'button', + className = '', + disabled, + children, + ...props }: ButtonHTMLAttributes) { - return ( - - ); + return ( + + ); } diff --git a/frontend/src/Components/StatsCard.tsx b/frontend/src/Components/StatsCard.tsx index 87a59323..59e4342c 100644 --- a/frontend/src/Components/StatsCard.tsx +++ b/frontend/src/Components/StatsCard.tsx @@ -1,21 +1,21 @@ export default function StatsCard({ - title, - number, - label, + title, + number, + label }: { - title: string; - number: string; - label: string; + title: string; + number: string; + label: string; }) { - return ( -
    -

    {title.toUpperCase()}

    -

    - {number} - - {label.toLowerCase()} - -

    -
    - ); + return ( +
    +

    {title.toUpperCase()}

    +

    + {number} + + {label.toLowerCase()} + +

    +
    + ); } diff --git a/frontend/src/Components/ThemeContext.tsx b/frontend/src/Components/ThemeContext.tsx index 33a7a59d..38eb0b5f 100644 --- a/frontend/src/Components/ThemeContext.tsx +++ b/frontend/src/Components/ThemeContext.tsx @@ -1,42 +1,42 @@ -import React, { createContext, useState, useEffect, ReactNode } from "react"; +import React, { createContext, useState, useEffect, ReactNode } from 'react'; interface ThemeContextType { - theme: string; - toggleTheme: () => void; + theme: string; + toggleTheme: () => void; } const defaultContext: ThemeContextType = { - theme: "light", - toggleTheme: () => {}, + theme: 'light', + toggleTheme: () => {} }; export const ThemeContext = createContext(defaultContext); interface ThemeProviderProps { - children: ReactNode; + children: ReactNode; } export const ThemeProvider: React.FC = ({ children }) => { - const [theme, setTheme] = useState("light"); - - useEffect(() => { - const storedTheme = localStorage.getItem("theme"); - if (storedTheme) { - setTheme(storedTheme); - document.documentElement.setAttribute("data-theme", storedTheme); - } - }, []); - - const toggleTheme = () => { - const newTheme = theme === "light" ? "dark" : "light"; - setTheme(newTheme); - localStorage.setItem("theme", newTheme); - document.documentElement.setAttribute("data-theme", newTheme); - }; - - return ( - - {children} - - ); + const [theme, setTheme] = useState('light'); + + useEffect(() => { + const storedTheme = localStorage.getItem('theme'); + if (storedTheme) { + setTheme(storedTheme); + document.documentElement.setAttribute('data-theme', storedTheme); + } + }, []); + + const toggleTheme = () => { + const newTheme = theme === 'light' ? 'dark' : 'light'; + setTheme(newTheme); + localStorage.setItem('theme', newTheme); + document.documentElement.setAttribute('data-theme', newTheme); + }; + + return ( + + {children} + + ); }; diff --git a/frontend/src/Components/ThemeToggle.tsx b/frontend/src/Components/ThemeToggle.tsx index 603f28c8..3bdc0a31 100644 --- a/frontend/src/Components/ThemeToggle.tsx +++ b/frontend/src/Components/ThemeToggle.tsx @@ -1,24 +1,24 @@ -import { useEffect, useContext } from "react"; -import { ThemeContext } from "./ThemeContext"; +import { useEffect, useContext } from 'react'; +import { ThemeContext } from './ThemeContext'; export default function ThemeToggle() { - const { theme, toggleTheme } = useContext(ThemeContext); + const { theme, toggleTheme } = useContext(ThemeContext); - useEffect(() => { - /* Sets the data-theme attribute on html tag */ - document.documentElement.setAttribute("data-theme", theme); - }, [theme]); + useEffect(() => { + /* Sets the data-theme attribute on html tag */ + document.documentElement.setAttribute('data-theme', theme); + }, [theme]); - return ( - /* Component provided by daisyUI - https://daisyui.com/components/toggle/ */ - { - toggleTheme(); - e.target.blur(); - }} - /> - ); + return ( + /* Component provided by daisyUI - https://daisyui.com/components/toggle/ */ + { + toggleTheme(); + e.target.blur(); + }} + /> + ); } diff --git a/frontend/src/Components/Toast.tsx b/frontend/src/Components/Toast.tsx index df6acba0..7b1d0508 100644 --- a/frontend/src/Components/Toast.tsx +++ b/frontend/src/Components/Toast.tsx @@ -1,52 +1,52 @@ import { - CheckCircleIcon, - ExclamationCircleIcon, -} from "@heroicons/react/24/outline"; -import { useEffect, useState } from "react"; + CheckCircleIcon, + ExclamationCircleIcon +} from '@heroicons/react/24/outline'; +import { useEffect, useState } from 'react'; // params needed: error or success, message, isVisible interface ToastProps { - state: ToastState; - message: string; - reset: () => void; + state: ToastState; + message: string; + reset: () => void; } export enum ToastState { - success = "success", - error = "error", - null = "", + success = 'success', + error = 'error', + null = '' } export default function Toast({ state, message, reset }: ToastProps) { - const [isVisible, setIsVisible] = useState(true); + const [isVisible, setIsVisible] = useState(true); - useEffect(() => { - const timeoutId = setTimeout(() => { - setIsVisible(false); - setTimeout(() => { - reset(); - }, 1000); - }, 5000); + useEffect(() => { + const timeoutId = setTimeout(() => { + setIsVisible(false); + setTimeout(() => { + reset(); + }, 1000); + }, 5000); - return () => { - clearTimeout(timeoutId); - }; - }, [reset]); + return () => { + clearTimeout(timeoutId); + }; + }, [reset]); - return ( -
    -
    - {state == "success" ? ( - - ) : ( - - )} - {message} -
    -
    - ); + return ( +
    +
    + {state == 'success' ? ( + + ) : ( + + )} + {message} +
    +
    + ); } diff --git a/frontend/src/Components/ToggleView.tsx b/frontend/src/Components/ToggleView.tsx index 519b9ee7..bb4f8fab 100644 --- a/frontend/src/Components/ToggleView.tsx +++ b/frontend/src/Components/ToggleView.tsx @@ -1,35 +1,35 @@ -import { ListBulletIcon, Squares2X2Icon } from "@heroicons/react/24/solid"; +import { ListBulletIcon, Squares2X2Icon } from '@heroicons/react/24/solid'; export enum ViewType { - Grid = "Grid", - List = "List", + Grid = 'Grid', + List = 'List' } export default function ToggleView({ - activeView, - setActiveView, + activeView, + setActiveView }: { - activeView: ViewType; - setActiveView: Function; + activeView: ViewType; + setActiveView: Function; }) { - return ( -
    - -
    - {/* TO DO: come back and render on active or not */} - - -
    -
    - ); + return ( +
    + +
    + {/* TO DO: come back and render on active or not */} + + +
    +
    + ); } diff --git a/frontend/src/Components/TopProgActivityPieChart.tsx b/frontend/src/Components/TopProgActivityPieChart.tsx index 247672fa..1d272204 100644 --- a/frontend/src/Components/TopProgActivityPieChart.tsx +++ b/frontend/src/Components/TopProgActivityPieChart.tsx @@ -1,37 +1,40 @@ -import { PieChart, Pie, Legend, Cell, ResponsiveContainer } from "recharts"; -import { ThemeContext } from "./ThemeContext"; -import { useContext } from "react"; +import { PieChart, Pie, Legend, Cell, ResponsiveContainer } from 'recharts'; +import { ThemeContext } from './ThemeContext'; +import { useContext } from 'react'; export default function TopProgPieChart({ data }: { data: any }) { - const { theme } = useContext(ThemeContext); + const { theme } = useContext(ThemeContext); - var COLORS = ["#D7F4F1", "#B0DFDA", "#18ABA0", "#005952", "#002E2A"]; - if (theme == "dark") { - COLORS = ["#11554E", "#13746C", "#14958A", "#61BAB2", "#B0DFDA"]; - } + var COLORS = ['#D7F4F1', '#B0DFDA', '#18ABA0', '#005952', '#002E2A']; + if (theme == 'dark') { + COLORS = ['#11554E', '#13746C', '#14958A', '#61BAB2', '#B0DFDA']; + } - console.log(data); + console.log(data); - return ( - - - - {data.map((_, index) => ( - - ))} - - - - - ); + return ( + + + + {data.map((_, index) => ( + + ))} + + + + + ); } diff --git a/frontend/src/Components/UserActivityMap.tsx b/frontend/src/Components/UserActivityMap.tsx index ca341b35..aeee4e26 100644 --- a/frontend/src/Components/UserActivityMap.tsx +++ b/frontend/src/Components/UserActivityMap.tsx @@ -1,365 +1,374 @@ -import { useState } from "react"; -import DropdownControl from "./inputs/DropdownControl"; -import useSWR from "swr"; -import { useAuth } from "@/AuthContext"; -import { ServerResponse } from "@/common"; -import convertSeconds from "./ConvertSeconds"; +import { useState } from 'react'; +import DropdownControl from './inputs/DropdownControl'; +import useSWR from 'swr'; +import { useAuth } from '@/AuthContext'; +import { ServerResponse } from '@/common'; +import convertSeconds from './ConvertSeconds'; type ActivityMapData = { - date: string; - total_time: string; - quartile: number; + date: string; + total_time: string; + quartile: number; }; /* interface for dynamically generated year options */ interface ValidYears { - [year: string]: string; + [year: string]: string; } /* subtract a year from a date */ const subtractYear = (date: Date) => { - const newDate = new Date(date); - newDate.setUTCFullYear(date.getUTCFullYear() - 1); - newDate.setUTCDate(newDate.getUTCDate() + 1); - return newDate; + const newDate = new Date(date); + newDate.setUTCFullYear(date.getUTCFullYear() - 1); + newDate.setUTCDate(newDate.getUTCDate() + 1); + return newDate; }; /* predefining tailwind strings lets us assign them dynamically later */ const quartileColors: string[] = [ - "bg-inner-background", - "bg-teal-1", - "bg-teal-2", - "bg-teal-3", - "bg-teal-4", + 'bg-inner-background', + 'bg-teal-1', + 'bg-teal-2', + 'bg-teal-3', + 'bg-teal-4' ]; /* node sizes for the activity map */ const nodeSizes: string = - "w-1.5 h-1.5 rounded-sm text-xs md:w-2 md:h-2 lg:w-3 lg:h-3 xl:w-3.5 xl:h-3.5 xl:rounded-md"; + 'w-1.5 h-1.5 rounded-sm text-xs md:w-2 md:h-2 lg:w-3 lg:h-3 xl:w-3.5 xl:h-3.5 xl:rounded-md'; /* gaps between cells in activity map */ -const gapSizes: string = "p-0 ml-px mt-px md:m-0 md:p-px"; +const gapSizes: string = 'p-0 ml-px mt-px md:m-0 md:p-px'; /* main component for the user activity map */ export default function UserActivityMap() { - const { user } = useAuth(); + const { user } = useAuth(); - const createdAtYear = parseInt(user?.created_at.substr(0, 4)); + const createdAtYear = parseInt(user?.created_at.substr(0, 4)); - const [yearEnd, setYearEnd] = useState(new Date()); - const [dropdownValDesc, setDropdownValDesc] = useState("the past year"); + const [yearEnd, setYearEnd] = useState(new Date()); + const [dropdownValDesc, setDropdownValDesc] = useState('the past year'); - const { data, error, isLoading } = useSWR>( - `/api/users/${user.id}/daily-activity${dropdownValDesc !== "the past year" ? "?year=" + dropdownValDesc.trim() : ""}`, - ); + const { data, error, isLoading } = useSWR>( + `/api/users/${user.id}/daily-activity${dropdownValDesc !== 'the past year' ? '?year=' + dropdownValDesc.trim() : ''}` + ); - const generateYearOptions = () => { - const years: ValidYears = { "Past year": "Past year" }; - const currentYear = new Date().getUTCFullYear(); - let i = currentYear - createdAtYear; - for (i; i >= 0; i--) { - /* added " " to change listed order without impacting appearance */ - const str = " " + String(createdAtYear + i); - years[str] = str; - } - return years; - }; + const generateYearOptions = () => { + const years: ValidYears = { 'Past year': 'Past year' }; + const currentYear = new Date().getUTCFullYear(); + let i = currentYear - createdAtYear; + for (i; i >= 0; i--) { + /* added " " to change listed order without impacting appearance */ + const str = ' ' + String(createdAtYear + i); + years[str] = str; + } + return years; + }; - const dropdownChange = (val: string) => { - let date; - if (val == "Past year") { - date = new Date(); - setYearEnd(date); - val = "the past year"; - } else { - date = new Date(Number(val), 0, 1); - setYearEnd(new Date(Number(val), 11, 31)); - } - setDropdownValDesc(val); - }; + const dropdownChange = (val: string) => { + let date; + if (val == 'Past year') { + date = new Date(); + setYearEnd(date); + val = 'the past year'; + } else { + date = new Date(Number(val), 0, 1); + setYearEnd(new Date(Number(val), 11, 31)); + } + setDropdownValDesc(val); + }; - /* generate nodes for the legend */ - const legendNodes: JSX.Element[] = []; - for (let i = 0; i < 5; i++) { - legendNodes.push(); - } + /* generate nodes for the legend */ + const legendNodes: JSX.Element[] = []; + for (let i = 0; i < 5; i++) { + legendNodes.push(); + } - const yearOptions = generateYearOptions(); + const yearOptions = generateYearOptions(); - return ( - //
    -
    -
    -
    - - -
    - {/* Could put something here, like "Keep up the good work!" */} -
    -
    -
    Less
    - {legendNodes.map((node) => node)} -
    More
    -
    -
    - {/*
    */} -
    - {error ? ( -
    -
    - Error loading activity. Please try again later. + return ( + //
    +
    +
    +
    + + +
    + {/* Could put something here, like "Keep up the good work!" */} +
    +
    +
    Less
    + {legendNodes.map((node) => node)} +
    More
    +
    -
    - ) : isLoading ? ( -
    - -
    - ) : ( - data && ( -
    - + {/*
    */} +
    + {error ? ( +
    +
    + Error loading activity. Please try again later. +
    +
    + ) : isLoading ? ( +
    + +
    + ) : ( + data && ( +
    + +
    + ) + )}
    - ) - )} -
    -
    - ); +
    + ); } /* component for the cells of our activity map */ function Node({ - quartile, - date, - activity_time, + quartile, + date, + activity_time }: { - quartile: number; - date?: Date; - activity_time?: string; + quartile: number; + date?: Date; + activity_time?: string; }) { - let tooltipString = ""; - if (!date) { - /* if node is from legend */ + let tooltipString = ''; + if (!date) { + /* if node is from legend */ + return ( +
    + ); + } + if (!activity_time) { + tooltipString = 'No activity on ' + date.toISOString().split('T')[0]; + } else { + tooltipString = `${activity_time} on ${date?.toISOString().split('T')[0]}`; + } return ( -
    +
    +
    +
    ); - } - if (!activity_time) { - tooltipString = "No activity on " + date.toISOString().split("T")[0]; - } else { - tooltipString = `${activity_time} on ${date?.toISOString().split("T")[0]}`; - } - return ( -
    -
    -
    - ); } /* component that builds the activity map table */ function ActivityMapTable({ - data, - end, - error, - isLoading, - range, + data, + end, + error, + isLoading, + range }: { - data: ActivityMapData[]; - end: Date; - error: any; - isLoading: boolean; - range: string; + data: ActivityMapData[]; + end: Date; + error: any; + isLoading: boolean; + range: string; }) { - /* array that holds cells for the table */ - const tableData: JSX.Element[] = []; - const tableMonths: string[] = []; - let i; - const len = data?.length; - // const now = new Date(); - let aggregateActivityTime = 0; - let oldMonth, newMonth; + /* array that holds cells for the table */ + const tableData: JSX.Element[] = []; + const tableMonths: string[] = []; + let i; + const len = data?.length; + // const now = new Date(); + let aggregateActivityTime = 0; + let oldMonth, newMonth; - const getAggregateActivityTime = (time: number) => { - if (error) { - return "Error fetching activity data"; - } - if (isLoading) { - return "Loading..."; - } else if (time == 0) { - return "No activity "; - } else if (time == 1) { - return "You have completed one hour of activity"; - } else { - return "You have completed " + String(time) + " hours of activity"; - } - }; + const getAggregateActivityTime = (time: number) => { + if (error) { + return 'Error fetching activity data'; + } + if (isLoading) { + return 'Loading...'; + } else if (time == 0) { + return 'No activity '; + } else if (time == 1) { + return 'You have completed one hour of activity'; + } else { + return 'You have completed ' + String(time) + ' hours of activity'; + } + }; - const insertMonthHeader = (date: Date) => { - const months = [ - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec", - ]; - tableMonths.push(months[date.getUTCMonth()]); - }; + const insertMonthHeader = (date: Date) => { + const months = [ + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec' + ]; + tableMonths.push(months[date.getUTCMonth()]); + }; - const dateCount = subtractYear(end); + const dateCount = subtractYear(end); - /* add spacers for days of week before activity range */ - for (i = 0; i < dateCount.getUTCDay(); i++) { - tableData.push( - -
    - , - ); - } + /* add spacers for days of week before activity range */ + for (i = 0; i < dateCount.getUTCDay(); i++) { + tableData.push( + +
    + + ); + } - i = new Date().getDay() + 1; + i = new Date().getDay() + 1; - /* fill first months array element if range starts midweek */ - if (dateCount.getUTCDay() != 0) { - if (dateCount.getUTCDate() < 8) { - insertMonthHeader(dateCount); - } else { - tableMonths.push(""); + /* fill first months array element if range starts midweek */ + if (dateCount.getUTCDay() != 0) { + if (dateCount.getUTCDate() < 8) { + insertMonthHeader(dateCount); + } else { + tableMonths.push(''); + } } - } - /* fill cells with Nodes for days in range */ - while (dateCount <= end) { - /* at start of new week, if new, push month, else push empty string */ - if (dateCount.getUTCDay() == 0) { - newMonth = dateCount.getUTCMonth(); - if ( - newMonth != oldMonth && - dateCount.getUTCDate() < 8 && - tableMonths[tableMonths.length - 1] == "" - ) { - insertMonthHeader(dateCount); - } else { - tableMonths.push(""); - } - oldMonth = newMonth; - } + /* fill cells with Nodes for days in range */ + while (dateCount <= end) { + /* at start of new week, if new, push month, else push empty string */ + if (dateCount.getUTCDay() == 0) { + newMonth = dateCount.getUTCMonth(); + if ( + newMonth != oldMonth && + dateCount.getUTCDate() < 8 && + tableMonths[tableMonths.length - 1] == '' + ) { + insertMonthHeader(dateCount); + } else { + tableMonths.push(''); + } + oldMonth = newMonth; + } - /* in range and activity on date */ - if ( - i < len && - dateCount.toISOString().split("T")[0] == data[i].date.split("T")[0] - ) { - const activityTime = convertSeconds(Number(data[i].total_time)); - tableData.push( - - - , - ); - aggregateActivityTime += Number(data[i].total_time); - i += 1; - } else { - tableData.push( - - - , - ); + /* in range and activity on date */ + if ( + i < len && + dateCount.toISOString().split('T')[0] == data[i].date.split('T')[0] + ) { + const activityTime = convertSeconds(Number(data[i].total_time)); + tableData.push( + + + + ); + aggregateActivityTime += Number(data[i].total_time); + i += 1; + } else { + tableData.push( + + + + ); + } + dateCount.setUTCDate(dateCount.getUTCDate() + 1); } - dateCount.setUTCDate(dateCount.getUTCDate() + 1); - } - /* add empty cells for days of week after activity range */ - for (i = dateCount.getUTCDay(); i < 7; i++) { - tableData.push( - -
    - , - ); - } - return ( - <> - - - - {tableMonths.map((node, index) => { - return ( - - ); - })} - - -
    - {node} -
    - - - - - - - - - - - - {tableData.map((_, index) => { - if (index % 7 == 0) { - return ( - - {tableData.slice(index, index + 7).map((node) => { - return node; - })} - - ); - } - })} - -
    -
    -
    -
    M
    -
    -
    + /* add empty cells for days of week after activity range */ + for (i = dateCount.getUTCDay(); i < 7; i++) { + tableData.push( +
    +
    -
    W
    -
    -
    -
    -
    F
    -
    -
    -
    -
    - {isLoading || error - ? null - : getAggregateActivityTime(Math.floor(aggregateActivityTime / 3600)) + - " in " + - range + - "."} -
    - - ); + ); + } + return ( + <> + + + + {tableMonths.map((node, index) => { + return ( + + ); + })} + + +
    + {node} +
    + + + + + + + + + + + + {tableData.map((_, index) => { + if (index % 7 == 0) { + return ( + + {tableData + .slice(index, index + 7) + .map((node) => { + return node; + })} + + ); + } + })} + +
    +
    +
    +
    M
    +
    +
    +
    +
    W
    +
    +
    +
    +
    F
    +
    +
    +
    +
    + {isLoading || error + ? null + : getAggregateActivityTime( + Math.floor(aggregateActivityTime / 3600) + ) + + ' in ' + + range + + '.'} +
    + + ); } diff --git a/frontend/src/Components/WeeklyActivity.tsx b/frontend/src/Components/WeeklyActivity.tsx index a899b8e6..10f46a4d 100644 --- a/frontend/src/Components/WeeklyActivity.tsx +++ b/frontend/src/Components/WeeklyActivity.tsx @@ -1,104 +1,105 @@ import { - LineChart, - Line, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - ResponsiveContainer, -} from "recharts"; -import { ThemeContext } from "./ThemeContext"; -import { useContext } from "react"; -import { RecentActivity } from "@/common"; + LineChart, + Line, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + ResponsiveContainer +} from 'recharts'; +import { ThemeContext } from './ThemeContext'; +import { useContext } from 'react'; +import { RecentActivity } from '@/common'; const WeekActivityChart = ({ data }: { data: any }) => { - const { theme } = useContext(ThemeContext); - var lineColor = theme == "light" ? "#18ABA0" : "#61BAB2"; - var gridColor = theme == "light" ? "#ECECEC" : "#737373"; - var backgroundColor = theme == "light" ? "#FFFFFF" : "#0F2926"; + const { theme } = useContext(ThemeContext); + var lineColor = theme == 'light' ? '#18ABA0' : '#61BAB2'; + var gridColor = theme == 'light' ? '#ECECEC' : '#737373'; + var backgroundColor = theme == 'light' ? '#FFFFFF' : '#0F2926'; - const result: RecentActivity[] = Array.from({ length: 7 }); - let currentDate = new Date(); + const result: RecentActivity[] = Array.from({ length: 7 }); + let currentDate = new Date(); - for (let i = 6; i >= 0; i--) { - let date = new Date(currentDate); - date.setDate(date.getDate() - i); - const dateString = date.toISOString().split("T")[0]; - let entry = data.find( - (activity: RecentActivity) => activity.date.split("T")[0] === dateString, - ); - entry - ? (entry = { - date: entry.date.split("T")[0], - delta: Math.round(entry.delta / 60), - }) - : (entry = { date: dateString, delta: 0 }); - result[6 - i] = entry; - } + for (let i = 6; i >= 0; i--) { + let date = new Date(currentDate); + date.setDate(date.getDate() - i); + const dateString = date.toISOString().split('T')[0]; + let entry = data.find( + (activity: RecentActivity) => + activity.date.split('T')[0] === dateString + ); + entry + ? (entry = { + date: entry.date.split('T')[0], + delta: Math.round(entry.delta / 60) + }) + : (entry = { date: dateString, delta: 0 }); + result[6 - i] = entry; + } - const weekdays = [ - "Sun", - "Mon", - "Tues", - "Wed", - "Thurs", - "Fri", - "Sat", - "Today", - ]; - const XAxisTick = (props) => { - const { x, y, payload } = props; - let day = new Date(payload.value).getDay() + 1; - if (new Date().getDay() == day) day = 7; + const weekdays = [ + 'Sun', + 'Mon', + 'Tues', + 'Wed', + 'Thurs', + 'Fri', + 'Sat', + 'Today' + ]; + const XAxisTick = (props) => { + const { x, y, payload } = props; + let day = new Date(payload.value).getDay() + 1; + if (new Date().getDay() == day) day = 7; + return ( + + + {weekdays[day]} + + + ); + }; return ( - - - {weekdays[day]} - - + + + + } /> + + + + + ); - }; - return ( - - - - } /> - - - - - - ); }; export default WeekActivityChart; diff --git a/frontend/src/Components/forms/AddCategoryForm.tsx b/frontend/src/Components/forms/AddCategoryForm.tsx index 8bfbcf73..0816631b 100644 --- a/frontend/src/Components/forms/AddCategoryForm.tsx +++ b/frontend/src/Components/forms/AddCategoryForm.tsx @@ -1,44 +1,44 @@ -import { useState } from "react"; -import { useForm, SubmitHandler } from "react-hook-form"; -import { TextInput, CloseX, SubmitButton } from "../inputs"; +import { useState } from 'react'; +import { useForm, SubmitHandler } from 'react-hook-form'; +import { TextInput, CloseX, SubmitButton } from '../inputs'; type Inputs = { - title: string; + title: string; }; export default function AddCategoryForm({ - onSuccess, + onSuccess }: { - onSuccess: (title: string) => void; + onSuccess: (title: string) => void; }) { - const [errorMessage, _] = useState(""); + const [errorMessage, _] = useState(''); - const { - register, - handleSubmit, - reset, - formState: { errors }, - } = useForm(); + const { + register, + handleSubmit, + reset, + formState: { errors } + } = useForm(); - const onSubmit: SubmitHandler = async (data) => { - onSuccess(data.title); - reset(); - }; + const onSubmit: SubmitHandler = async (data) => { + onSuccess(data.title); + reset(); + }; - return ( -
    - reset()} /> -
    - - - -
    - ); + return ( +
    + reset()} /> +
    + + + +
    + ); } diff --git a/frontend/src/Components/forms/AddLinkForm.tsx b/frontend/src/Components/forms/AddLinkForm.tsx index ccde44a7..018eabe3 100644 --- a/frontend/src/Components/forms/AddLinkForm.tsx +++ b/frontend/src/Components/forms/AddLinkForm.tsx @@ -1,54 +1,54 @@ -import { useState } from "react"; -import { useForm, SubmitHandler } from "react-hook-form"; -import { SubmitButton } from "../inputs/SubmitButton"; -import { TextInput } from "../inputs/TextInput"; -import { CloseX } from "../inputs/CloseX"; +import { useState } from 'react'; +import { useForm, SubmitHandler } from 'react-hook-form'; +import { SubmitButton } from '../inputs/SubmitButton'; +import { TextInput } from '../inputs/TextInput'; +import { CloseX } from '../inputs/CloseX'; type Inputs = { - title: string; - url: string; + title: string; + url: string; }; export default function AddLinkForm({ - onSuccess, + onSuccess }: { - onSuccess: (title: string, url: string) => void; + onSuccess: (title: string, url: string) => void; }) { - const [errorMessage, _] = useState(""); + const [errorMessage, _] = useState(''); - const { - register, - handleSubmit, - reset, - formState: { errors }, - } = useForm(); + const { + register, + handleSubmit, + reset, + formState: { errors } + } = useForm(); - const onSubmit: SubmitHandler = async (data) => { - onSuccess(data.title, data.url); - reset(); - }; + const onSubmit: SubmitHandler = async (data) => { + onSuccess(data.title, data.url); + reset(); + }; - return ( -
    - reset()} /> -
    - - - - -
    - ); + return ( +
    + reset()} /> +
    + + + + +
    + ); } diff --git a/frontend/src/Components/forms/AddProviderForm.tsx b/frontend/src/Components/forms/AddProviderForm.tsx index 5df49013..5df77bf8 100644 --- a/frontend/src/Components/forms/AddProviderForm.tsx +++ b/frontend/src/Components/forms/AddProviderForm.tsx @@ -1,128 +1,131 @@ -import { ProviderPlatformState, ProviderPlatformType } from "@/common"; -import axios from "axios"; -import { useState } from "react"; -import { SubmitHandler, useForm } from "react-hook-form"; +import { ProviderPlatformState, ProviderPlatformType } from '@/common'; +import axios from 'axios'; +import { useState } from 'react'; +import { SubmitHandler, useForm } from 'react-hook-form'; import { - CloseX, - DropdownInput, - SubmitButton, - TextAreaInput, - TextInput, -} from "../inputs"; -import { ToastState } from "../Toast"; + CloseX, + DropdownInput, + SubmitButton, + TextAreaInput, + TextInput +} from '../inputs'; +import { ToastState } from '../Toast'; type ProviderInputs = { - name: string; - type: ProviderPlatformType; - description: string; - base_url: string; - account_id: string; - access_key: string; - icon_url: string; - state: ProviderPlatformState; + name: string; + type: ProviderPlatformType; + description: string; + base_url: string; + account_id: string; + access_key: string; + icon_url: string; + state: ProviderPlatformState; }; export default function AddProviderForm({ - onSuccess, + onSuccess }: { - onSuccess: Function; + onSuccess: Function; }) { - const [errorMessage, setErrorMessage] = useState(""); + const [errorMessage, setErrorMessage] = useState(''); - const { - register, - handleSubmit, - reset, - formState: { errors }, - } = useForm(); + const { + register, + handleSubmit, + reset, + formState: { errors } + } = useForm(); - const onSubmit: SubmitHandler = async (data) => { - try { - if (!data.base_url.startsWith("http")) { - data.base_url = "https://" + data.base_url; - } - setErrorMessage(""); - const response = await axios.post("/api/provider-platforms", data); - if (response.status !== 201) { - onSuccess(ToastState.error, "Failed to add provider platform"); - } - reset(); - onSuccess(ToastState.success, "Provider platform created successfully"); - } catch (error: any) { - setErrorMessage(error.response.data.message); - } - }; + const onSubmit: SubmitHandler = async (data) => { + try { + if (!data.base_url.startsWith('http')) { + data.base_url = 'https://' + data.base_url; + } + setErrorMessage(''); + const response = await axios.post('/api/provider-platforms', data); + if (response.status !== 201) { + onSuccess(ToastState.error, 'Failed to add provider platform'); + } + reset(); + onSuccess( + ToastState.success, + 'Provider platform created successfully' + ); + } catch (error: any) { + setErrorMessage(error.response.data.message); + } + }; - return ( -
    - reset()} /> -
    - - - - - - - - - - -
    - ); + return ( +
    + reset()} /> +
    + + + + + + + + + + +
    + ); } diff --git a/frontend/src/Components/forms/AddUserForm.tsx b/frontend/src/Components/forms/AddUserForm.tsx index 3dc6b56a..83ceb3be 100644 --- a/frontend/src/Components/forms/AddUserForm.tsx +++ b/frontend/src/Components/forms/AddUserForm.tsx @@ -1,142 +1,150 @@ -import { ProviderPlatform, UserRole } from "../../common"; -import axios from "axios"; -import { useEffect, useState } from "react"; -import { useForm, SubmitHandler } from "react-hook-form"; -import { ToastState } from "../Toast"; -import { CloseX } from "../inputs/CloseX"; -import { TextInput } from "../inputs/TextInput"; -import { DropdownInput } from "../inputs/DropdownInput"; -import { SubmitButton } from "../inputs/SubmitButton"; +import { ProviderPlatform, UserRole } from '../../common'; +import axios from 'axios'; +import { useEffect, useState } from 'react'; +import { useForm, SubmitHandler } from 'react-hook-form'; +import { ToastState } from '../Toast'; +import { CloseX } from '../inputs/CloseX'; +import { TextInput } from '../inputs/TextInput'; +import { DropdownInput } from '../inputs/DropdownInput'; +import { SubmitButton } from '../inputs/SubmitButton'; type Inputs = { - name_first: string; - name_last: string; - username: string; - role: UserRole; + name_first: string; + name_last: string; + username: string; + role: UserRole; }; export default function AddUserForm({ - onSuccess, + onSuccess }: { - onSuccess: (psw: string, msg: string, err: ToastState) => void; + onSuccess: (psw: string, msg: string, err: ToastState) => void; }) { - const [errorMessage, setErrorMessage] = useState(""); - const [providers, setProviders] = useState([]); - const [selectedProviders, setSelectedProviders] = useState([]); - const { - reset, - register, - handleSubmit, - formState: { errors }, - } = useForm(); + const [errorMessage, setErrorMessage] = useState(''); + const [providers, setProviders] = useState([]); + const [selectedProviders, setSelectedProviders] = useState([]); + const { + reset, + register, + handleSubmit, + formState: { errors } + } = useForm(); - const onSubmit: SubmitHandler = async (data) => { - try { - setErrorMessage(""); - const response = await axios.post("/api/users", { - user: data, - provider_platforms: selectedProviders, - }); + const onSubmit: SubmitHandler = async (data) => { + try { + setErrorMessage(''); + const response = await axios.post('/api/users', { + user: data, + provider_platforms: selectedProviders + }); - if (response.status !== 201) { - onSuccess("", "Failed to create user", ToastState.error); - } - reset(); - onSuccess( - response.data.temp_password, - "User created successfully with temporary password", - ToastState.success, - ); - } catch (error: any) { - setErrorMessage(error.response.data.message); - } - }; - - const handleAddUserToProviderList = (providerId: number) => { - if (selectedProviders.includes(providerId)) { - setSelectedProviders(selectedProviders.filter((id) => id !== providerId)); - } else { - setSelectedProviders([...selectedProviders, providerId]); - } - }; + if (response.status !== 201) { + onSuccess('', 'Failed to create user', ToastState.error); + } + reset(); + onSuccess( + response.data.temp_password, + 'User created successfully with temporary password', + ToastState.success + ); + } catch (error: any) { + setErrorMessage(error.response.data.message); + } + }; - useEffect(() => { - const fetchActiveProviders = async () => { - try { - const resp = await axios.get( - `/api/provider-platforms?only=oidc_enabled`, - ); - if (resp.status === 200) { - setProviders(resp.data.data); + const handleAddUserToProviderList = (providerId: number) => { + if (selectedProviders.includes(providerId)) { + setSelectedProviders( + selectedProviders.filter((id) => id !== providerId) + ); + } else { + setSelectedProviders([...selectedProviders, providerId]); } - } catch (error: any) { - setErrorMessage(error.response.data.message); - } }; - fetchActiveProviders(); - }, []); - return ( -
    - reset()} /> -
    - - - - -
    - {providers && - providers.map((provider: ProviderPlatform) => ( -
    -
    - Create New Account for User in: -
    -
    - -
    -
    - ))} - - -
    - ); + useEffect(() => { + const fetchActiveProviders = async () => { + try { + const resp = await axios.get( + `/api/provider-platforms?only=oidc_enabled` + ); + if (resp.status === 200) { + setProviders(resp.data.data); + } + } catch (error: any) { + setErrorMessage(error.response.data.message); + } + }; + fetchActiveProviders(); + }, []); + + return ( +
    + reset()} /> +
    + + + + +
    + {providers && + providers.map((provider: ProviderPlatform) => ( +
    +
    + Create New Account for User in: +
    +
    + +
    +
    + ))} + + +
    + ); } diff --git a/frontend/src/Components/forms/ChangePasswordForm.tsx b/frontend/src/Components/forms/ChangePasswordForm.tsx index d9abdcef..0bdc9ae7 100644 --- a/frontend/src/Components/forms/ChangePasswordForm.tsx +++ b/frontend/src/Components/forms/ChangePasswordForm.tsx @@ -1,200 +1,200 @@ -import { useEffect, useState } from "react"; -import { useForm, useWatch, SubmitHandler } from "react-hook-form"; -import InputError from "../../Components/inputs/InputError"; -import PrimaryButton from "../../Components/PrimaryButton"; -import { TextInput } from "../../Components/inputs/TextInput"; -import { CheckIcon, XMarkIcon } from "@heroicons/react/24/solid"; +import { useEffect, useState } from 'react'; +import { useForm, useWatch, SubmitHandler } from 'react-hook-form'; +import InputError from '../../Components/inputs/InputError'; +import PrimaryButton from '../../Components/PrimaryButton'; +import { TextInput } from '../../Components/inputs/TextInput'; +import { CheckIcon, XMarkIcon } from '@heroicons/react/24/solid'; -import axios from "axios"; -import { useAuth } from "@/AuthContext"; +import axios from 'axios'; +import { useAuth } from '@/AuthContext'; type Inputs = { - password: string; - confirm: string; - facility_name: string; + password: string; + confirm: string; + facility_name: string; }; export default function ChangePasswordForm() { - const [errorMessage, setErrorMessage] = useState(""); - const [processing, setProcessing] = useState(false); - const [isFacilityDefault, setIsFacilityDefault] = useState(null); - const auth = useAuth(); + const [errorMessage, setErrorMessage] = useState(''); + const [processing, setProcessing] = useState(false); + const [isFacilityDefault, setIsFacilityDefault] = useState(null); + const auth = useAuth(); - const { - control, - register, - handleSubmit, - reset, - formState: { errors }, - } = useForm(); + const { + control, + register, + handleSubmit, + reset, + formState: { errors } + } = useForm(); - const password = useWatch({ - control, - name: "password", - }); + const password = useWatch({ + control, + name: 'password' + }); - const confirm = useWatch({ - control, - name: "confirm", - }); + const confirm = useWatch({ + control, + name: 'confirm' + }); - const facility = useWatch({ - control, - name: "facility_name", - }); - useEffect(() => { - const checkFacilityName = async () => { - try { - const resp = await axios.get("/api/facilities/1"); - if (resp.status === 200) { - setIsFacilityDefault(resp.data.data[0].name === "Default"); - return; - } - } catch (error: any) { - setIsFacilityDefault(false); - } - }; - checkFacilityName(); - }, []); + const facility = useWatch({ + control, + name: 'facility_name' + }); + useEffect(() => { + const checkFacilityName = async () => { + try { + const resp = await axios.get('/api/facilities/1'); + if (resp.status === 200) { + setIsFacilityDefault(resp.data.data[0].name === 'Default'); + return; + } + } catch (error: any) { + setIsFacilityDefault(false); + } + }; + checkFacilityName(); + }, []); - const isLengthValid = password && password.length >= 8; - const hasNumber = /\d/.test(password); - const passwordsMatch = password === confirm; - const isValid = isLengthValid && hasNumber && passwordsMatch; - const validFacility = - facility && facility.length > 2 && facility.trim().length > 2; - const isFirstAdminLogin = - auth.user.id === 1 && auth.user.role === "admin" && isFacilityDefault; + const isLengthValid = password && password.length >= 8; + const hasNumber = /\d/.test(password); + const passwordsMatch = password === confirm; + const isValid = isLengthValid && hasNumber && passwordsMatch; + const validFacility = + facility && facility.length > 2 && facility.trim().length > 2; + const isFirstAdminLogin = + auth.user.id === 1 && auth.user.role === 'admin' && isFacilityDefault; - const submit: SubmitHandler = async (data) => { - try { - setErrorMessage(""); - setProcessing(true); - if (data.facility_name) { - data.facility_name = data.facility_name.trim(); - } - const response = await axios.post("/api/reset-password", data); - if (response.status === 200) { - window.location.replace("dashboard"); - } else { - setErrorMessage(`Your passwords did not pass validation, + const submit: SubmitHandler = async (data) => { + try { + setErrorMessage(''); + setProcessing(true); + if (data.facility_name) { + data.facility_name = data.facility_name.trim(); + } + const response = await axios.post('/api/reset-password', data); + if (response.status === 200) { + window.location.replace('dashboard'); + } else { + setErrorMessage(`Your passwords did not pass validation, please check that they match and are 8 or more characters with at least 1 number.`); - } - } catch (error: any) { - setProcessing(false); - setErrorMessage( - error.response.data || - "Your passwords didn't pass validation, please try again.", - ); - reset(); - } - }; + } + } catch (error: any) { + setProcessing(false); + setErrorMessage( + error.response.data || + "Your passwords didn't pass validation, please try again." + ); + reset(); + } + }; - return ( -
    - + return ( + + -
    -

    - {isLengthValid ? ( - - ) : ( - - )}{" "} - Password is 8 or more characters -

    -

    - {hasNumber ? ( - - ) : ( - - )}{" "} - Password includes at least one number -

    -
    +
    +

    + {isLengthValid ? ( + + ) : ( + + )}{' '} + Password is 8 or more characters +

    +

    + {hasNumber ? ( + + ) : ( + + )}{' '} + Password includes at least one number +

    +
    - + -
    -

    - {passwordsMatch ? ( - - ) : ( - - )}{" "} - Passwords match -

    -
    +
    +

    + {passwordsMatch ? ( + + ) : ( + + )}{' '} + Passwords match +

    +
    - {errorMessage && ( -
    - -
    - )} + {errorMessage && ( +
    + +
    + )} - {isFirstAdminLogin && ( - <> - -
    -

    - {validFacility ? ( - - ) : ( - - )}{" "} - Valid facility name -

    -
    - - )} + {isFirstAdminLogin && ( + <> + +
    +

    + {validFacility ? ( + + ) : ( + + )}{' '} + Valid facility name +

    +
    + + )} -
    - - {processing ? ( - - ) : ( -
    Reset Password
    - )} -
    -
    - - ); +
    + + {processing ? ( + + ) : ( +
    Reset Password
    + )} +
    +
    + + ); } diff --git a/frontend/src/Components/forms/ConfirmImportAllUsersForm.tsx b/frontend/src/Components/forms/ConfirmImportAllUsersForm.tsx index 2ad757f0..71caf548 100644 --- a/frontend/src/Components/forms/ConfirmImportAllUsersForm.tsx +++ b/frontend/src/Components/forms/ConfirmImportAllUsersForm.tsx @@ -1,29 +1,31 @@ -import { CloseX } from "../inputs/CloseX"; +import { CloseX } from '../inputs/CloseX'; export default function ConfirmImportAllUsersForm({ - onCancel, - onSuccess, + onCancel, + onSuccess }: { - onCancel: Function; - onSuccess: Function; + onCancel: Function; + onSuccess: Function; }) { - return ( -
    - onCancel()} /> -

    Are you sure you would like to import all users?

    -
    - - -
    -
    - ); + return ( +
    + onCancel()} /> +

    + Are you sure you would like to import all users? +

    +
    + + +
    +
    + ); } diff --git a/frontend/src/Components/forms/ConsentForm.tsx b/frontend/src/Components/forms/ConsentForm.tsx index 5c48590d..5f12a7fd 100644 --- a/frontend/src/Components/forms/ConsentForm.tsx +++ b/frontend/src/Components/forms/ConsentForm.tsx @@ -1,50 +1,52 @@ -import axios from "axios"; +import axios from 'axios'; export default function ConsentForm() { - const accept = async () => { - try { - const urlParams = new URLSearchParams(window.location.search); - const consent = urlParams.get("consent_challenge"); - await axios(`/api/consent/accept`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - data: { consent_challenge: consent }, - }); - return; - } catch (error: any) { - console.error(error.response); - } - }; - const deny = () => { - window.location.replace("/"); - }; - return ( -
    -
    -

    Consent Form

    -

    - Do you consent to give this external application access to your - account? -

    -
    - - + const accept = async () => { + try { + const urlParams = new URLSearchParams(window.location.search); + const consent = urlParams.get('consent_challenge'); + await axios(`/api/consent/accept`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + data: { consent_challenge: consent } + }); + return; + } catch (error: any) { + console.error(error.response); + } + }; + const deny = () => { + window.location.replace('/'); + }; + return ( +
    +
    +

    + Consent Form +

    +

    + Do you consent to give this external application access to + your account? +

    +
    + + +
    +
    -
    -
    - ); + ); } diff --git a/frontend/src/Components/forms/DeleteForm.tsx b/frontend/src/Components/forms/DeleteForm.tsx index 2116a690..596f000f 100644 --- a/frontend/src/Components/forms/DeleteForm.tsx +++ b/frontend/src/Components/forms/DeleteForm.tsx @@ -1,32 +1,32 @@ -import { CloseX } from "../inputs"; +import { CloseX } from '../inputs'; interface DeleteProps { - item: string; - onCancel: () => void; - onSuccess: () => void; + item: string; + onCancel: () => void; + onSuccess: () => void; } export default function DeleteForm({ item, onCancel, onSuccess }: DeleteProps) { - return ( -
    - onCancel()} /> -

    - Are you sure you would like to delete this {item.toLowerCase()}? -
    This action cannot be undone. -

    -
    - - -
    -
    - ); + return ( +
    + onCancel()} /> +

    + Are you sure you would like to delete this {item.toLowerCase()}? +
    This action cannot be undone. +

    +
    + + +
    +
    + ); } diff --git a/frontend/src/Components/forms/EditProviderForm.tsx b/frontend/src/Components/forms/EditProviderForm.tsx index ac2c6b9d..22e73eb2 100644 --- a/frontend/src/Components/forms/EditProviderForm.tsx +++ b/frontend/src/Components/forms/EditProviderForm.tsx @@ -1,238 +1,247 @@ import { - ProviderPlatform, - ProviderPlatformState, - ProviderPlatformType, -} from "@/common"; -import { EyeIcon, EyeSlashIcon } from "@heroicons/react/24/solid"; -import axios from "axios"; -import { useState } from "react"; -import { SubmitHandler, useForm } from "react-hook-form"; + ProviderPlatform, + ProviderPlatformState, + ProviderPlatformType +} from '@/common'; +import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/solid'; +import axios from 'axios'; +import { useState } from 'react'; +import { SubmitHandler, useForm } from 'react-hook-form'; import { - CloseX, - TextInput, - TextAreaInput, - DropdownInput, - SubmitButton, -} from "../inputs"; -import { ToastState } from "../Toast"; + CloseX, + TextInput, + TextAreaInput, + DropdownInput, + SubmitButton +} from '../inputs'; +import { ToastState } from '../Toast'; type ProviderInputs = { - id: number; - name: string; - type: ProviderPlatformType; - description: string; - base_url: string; - account_id: string; - access_key: string; - icon_url: string; - state: ProviderPlatformState; + id: number; + name: string; + type: ProviderPlatformType; + description: string; + base_url: string; + account_id: string; + access_key: string; + icon_url: string; + state: ProviderPlatformState; }; export default function EditProviderForm({ - onSuccess, - provider, + onSuccess, + provider }: { - onSuccess: Function; - provider: ProviderPlatform; + onSuccess: Function; + provider: ProviderPlatform; }) { - const [errorMessage, setErrorMessage] = useState(""); - const [showAdditionalFields, setShowAdditionalFields] = useState(false); - const [showAccessKey, setShowAccessKey] = useState(false); - const [accessKey, setAccessKey] = useState(""); - const { - register, - handleSubmit, - reset, - formState: { errors }, - } = useForm({ - defaultValues: { - name: provider.name, - description: provider.description, - type: provider.type, - base_url: provider.base_url, - account_id: provider.account_id, - icon_url: provider.icon_url, - state: provider.state, - }, - }); - - const getAccessKey = async () => { - if (showAccessKey) { - setShowAccessKey(false); - return; - } - if (accessKey) { - setShowAccessKey(true); - return; - } - try { - const response = await axios.get( - `/api/provider-platforms/${provider.id}`, - ); - setAccessKey(response.data.data[0]["access_key"]); - setShowAccessKey(true); - } catch (error) { - console.log(error); - setErrorMessage(error.response.data.message); - } - }; - - function diffFormData(formData: any, currentUserData: any) { - const changes: Partial = {}; - Object.keys(formData).forEach((key) => { - if ( - formData[key] !== currentUserData[key] && - formData[key] !== undefined - ) { - changes[key] = formData[key]; - } + const [errorMessage, setErrorMessage] = useState(''); + const [showAdditionalFields, setShowAdditionalFields] = useState(false); + const [showAccessKey, setShowAccessKey] = useState(false); + const [accessKey, setAccessKey] = useState(''); + const { + register, + handleSubmit, + reset, + formState: { errors } + } = useForm({ + defaultValues: { + name: provider.name, + description: provider.description, + type: provider.type, + base_url: provider.base_url, + account_id: provider.account_id, + icon_url: provider.icon_url, + state: provider.state + } }); - return changes; - } - const onSubmit: SubmitHandler = async (data) => { - const cleanData = diffFormData(data, provider); - try { - setErrorMessage(""); - const response = await axios.patch( - `/api/provider-platforms/${provider?.id}`, - cleanData, - ); - if (response.status !== 201) { - onSuccess(ToastState.error, "Failed to update provider platform"); - } - reset(); - onSuccess(ToastState.success, "Provider platform updated successfully"); - } catch (error: any) { - setErrorMessage(error.response.data.message); - } - }; + const getAccessKey = async () => { + if (showAccessKey) { + setShowAccessKey(false); + return; + } + if (accessKey) { + setShowAccessKey(true); + return; + } + try { + const response = await axios.get( + `/api/provider-platforms/${provider.id}` + ); + setAccessKey(response.data.data[0]['access_key']); + setShowAccessKey(true); + } catch (error) { + console.log(error); + setErrorMessage(error.response.data.message); + } + }; - function closeAndReset() { - onSuccess(); - reset(); - } + function diffFormData(formData: any, currentUserData: any) { + const changes: Partial = {}; + Object.keys(formData).forEach((key) => { + if ( + formData[key] !== currentUserData[key] && + formData[key] !== undefined + ) { + changes[key] = formData[key]; + } + }); + return changes; + } - return ( -
    - closeAndReset()} /> -
    - - - - + const onSubmit: SubmitHandler = async (data) => { + const cleanData = diffFormData(data, provider); + try { + setErrorMessage(''); + const response = await axios.patch( + `/api/provider-platforms/${provider?.id}`, + cleanData + ); + if (response.status !== 201) { + onSuccess( + ToastState.error, + 'Failed to update provider platform' + ); + } + reset(); + onSuccess( + ToastState.success, + 'Provider platform updated successfully' + ); + } catch (error: any) { + setErrorMessage(error.response.data.message); + } + }; - {/* Button to toggle additional fields */} -
    - -
    + function closeAndReset() { + onSuccess(); + reset(); + } -
    - - -
    - ); + ); } diff --git a/frontend/src/Components/forms/EditUserForm.tsx b/frontend/src/Components/forms/EditUserForm.tsx index 84ebc267..827c9583 100644 --- a/frontend/src/Components/forms/EditUserForm.tsx +++ b/frontend/src/Components/forms/EditUserForm.tsx @@ -1,114 +1,114 @@ -import { User, UserRole } from "../../common"; -import axios from "axios"; -import { useState } from "react"; -import { useForm, SubmitHandler } from "react-hook-form"; -import { TextInput } from "../inputs/TextInput"; -import { DropdownInput } from "../inputs/DropdownInput"; -import { SubmitButton } from "../inputs/SubmitButton"; -import { CloseX } from "../inputs/CloseX"; +import { User, UserRole } from '../../common'; +import axios from 'axios'; +import { useState } from 'react'; +import { useForm, SubmitHandler } from 'react-hook-form'; +import { TextInput } from '../inputs/TextInput'; +import { DropdownInput } from '../inputs/DropdownInput'; +import { SubmitButton } from '../inputs/SubmitButton'; +import { CloseX } from '../inputs/CloseX'; type Inputs = { - name_first: string; - name_last: string; - username: string; - role: UserRole; - email: string; + name_first: string; + name_last: string; + username: string; + role: UserRole; + email: string; }; export default function EditUserForm({ - onSuccess, - user, + onSuccess, + user }: { - onSuccess: () => void; - user: User; + onSuccess: () => void; + user: User; }) { - const [errorMessage, setErrorMessage] = useState(""); + const [errorMessage, setErrorMessage] = useState(''); - const { - register, - handleSubmit, - formState: { errors }, - } = useForm({ - defaultValues: { - name_first: user.name_first, - name_last: user.name_last, - username: user.username, - role: user.role as UserRole, - email: user.email, - }, - }); - - function diffFormData(formData: any, currentUserData: any) { - const changes: Partial = {}; - Object.keys(formData).forEach((key) => { - if ( - formData[key] !== currentUserData[key] && - formData[key] !== undefined - ) { - changes[key] = formData[key]; - } + const { + register, + handleSubmit, + formState: { errors } + } = useForm({ + defaultValues: { + name_first: user.name_first, + name_last: user.name_last, + username: user.username, + role: user.role as UserRole, + email: user.email + } }); - return changes; - } - - const onSubmit: SubmitHandler = async (data) => { - try { - setErrorMessage(""); - const cleanData = diffFormData(data, user); - await axios.patch(`/api/users/${user.id}`, cleanData); - onSuccess(); - } catch (error: any) { - setErrorMessage(error.response.data.message); + function diffFormData(formData: any, currentUserData: any) { + const changes: Partial = {}; + Object.keys(formData).forEach((key) => { + if ( + formData[key] !== currentUserData[key] && + formData[key] !== undefined + ) { + changes[key] = formData[key]; + } + }); + return changes; } - }; - return ( - <> - onSuccess()} /> -
    - - - - - - - - - ); + const onSubmit: SubmitHandler = async (data) => { + try { + setErrorMessage(''); + const cleanData = diffFormData(data, user); + await axios.patch(`/api/users/${user.id}`, cleanData); + + onSuccess(); + } catch (error: any) { + setErrorMessage(error.response.data.message); + } + }; + + return ( + <> + onSuccess()} /> +
    + + + + + + + + + ); } diff --git a/frontend/src/Components/forms/LoginForm.tsx b/frontend/src/Components/forms/LoginForm.tsx index 75642b1b..8b92bd43 100644 --- a/frontend/src/Components/forms/LoginForm.tsx +++ b/frontend/src/Components/forms/LoginForm.tsx @@ -1,126 +1,129 @@ -import { useState } from "react"; -import { useForm, SubmitHandler } from "react-hook-form"; -import Checkbox from "../../Components/inputs/Checkbox"; -import InputError from "../../Components/inputs/InputError"; -import PrimaryButton from "../../Components/PrimaryButton"; -import { TextInput } from "../../Components/inputs/TextInput"; -import axios from "axios"; +import { useState } from 'react'; +import { useForm, SubmitHandler } from 'react-hook-form'; +import Checkbox from '../../Components/inputs/Checkbox'; +import InputError from '../../Components/inputs/InputError'; +import PrimaryButton from '../../Components/PrimaryButton'; +import { TextInput } from '../../Components/inputs/TextInput'; +import axios from 'axios'; type Inputs = { - identifier: string; - method: string; - password: string; + identifier: string; + method: string; + password: string; }; export default function LoginForm() { - const [errorMessage, setErrorMessage] = useState(""); - const [processing, setProcessing] = useState(false); - const { - register, - handleSubmit, - formState: { errors }, - } = useForm(); + const [errorMessage, setErrorMessage] = useState(''); + const [processing, setProcessing] = useState(false); + const { + register, + handleSubmit, + formState: { errors } + } = useForm(); - const submit: SubmitHandler = async (data) => { - try { - let kratosData = { identifier: data.identifier, password: data.password }; - const urlParams = new URLSearchParams(window.location.search); - const login_challenge = urlParams.get("login_challenge"); - const login_flow = urlParams.get("flow"); - if (!login_flow) { - console.error("No kratos login flow found"); - window.location.replace("/"); - return; - } - const flow_response = await axios.get( - "/self-service/login/flows?id=" + login_flow, - ); - setErrorMessage(""); - setProcessing(true); - const url = "/self-service/login?flow=" + login_flow; - if (login_challenge) { - kratosData["method"] = "oidc"; - } else { - kratosData["method"] = "password"; - } - const cookie = flow_response.data.ui.nodes[0].attributes.value; - kratosData["csrf_token"] = cookie; - const response = await axios(url, { - method: "POST", - data: kratosData, - headers: { - "Content-Type": "application/json", - "X-CSRF-Token": cookie, - }, - }); - if (response.data.session.active) { - const loginResp = await axios.post("/api/login", { - username: data.identifier, - password: data.password, - }); - const user = loginResp.data; - if (user.reset_password) { - window.location.replace("reset-password"); - return; - } else if (response.status === 200) { - window.location.replace("/dashboard"); - return; + const submit: SubmitHandler = async (data) => { + try { + let kratosData = { + identifier: data.identifier, + password: data.password + }; + const urlParams = new URLSearchParams(window.location.search); + const login_challenge = urlParams.get('login_challenge'); + const login_flow = urlParams.get('flow'); + if (!login_flow) { + console.error('No kratos login flow found'); + window.location.replace('/'); + return; + } + const flow_response = await axios.get( + '/self-service/login/flows?id=' + login_flow + ); + setErrorMessage(''); + setProcessing(true); + const url = '/self-service/login?flow=' + login_flow; + if (login_challenge) { + kratosData['method'] = 'oidc'; + } else { + kratosData['method'] = 'password'; + } + const cookie = flow_response.data.ui.nodes[0].attributes.value; + kratosData['csrf_token'] = cookie; + const response = await axios(url, { + method: 'POST', + data: kratosData, + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-Token': cookie + } + }); + if (response.data.session.active) { + const loginResp = await axios.post('/api/login', { + username: data.identifier, + password: data.password + }); + const user = loginResp.data; + if (user.reset_password) { + window.location.replace('reset-password'); + return; + } else if (response.status === 200) { + window.location.replace('/dashboard'); + return; + } + } else { + setErrorMessage('Login failed, session is not active'); + } + } catch (error: any) { + setProcessing(false); + setErrorMessage('login failed, please try again'); } - } else { - setErrorMessage("Login failed, session is not active"); - } - } catch (error: any) { - setProcessing(false); - setErrorMessage("login failed, please try again"); - } - }; + }; - return ( -
    - + return ( + + - + - {errorMessage && ( -
    - -
    - )} + {errorMessage && ( +
    + +
    + )} -
    - -
    +
    + +
    -
    - - {processing ? ( - - ) : ( -
    Log in
    - )} -
    -
    - - ); +
    + + {processing ? ( + + ) : ( +
    Log in
    + )} +
    +
    + + ); } diff --git a/frontend/src/Components/forms/MapUserForm.tsx b/frontend/src/Components/forms/MapUserForm.tsx index cd4f7f43..ccb3685d 100644 --- a/frontend/src/Components/forms/MapUserForm.tsx +++ b/frontend/src/Components/forms/MapUserForm.tsx @@ -1,176 +1,181 @@ -import axios from "axios"; -import { useEffect, useState } from "react"; -import { ToastState } from "../Toast"; -import { ProviderUser } from "@/common"; -import { User } from "@/types"; -import { CloseX } from "../inputs/CloseX"; +import axios from 'axios'; +import { useEffect, useState } from 'react'; +import { ToastState } from '../Toast'; +import { ProviderUser } from '@/common'; +import { User } from '@/types'; +import { CloseX } from '../inputs/CloseX'; interface Props { - externalUser: ProviderUser; - providerId: number; - onSubmit: (msg: string, err: ToastState) => void; - onCancel: () => void; + externalUser: ProviderUser; + providerId: number; + onSubmit: (msg: string, err: ToastState) => void; + onCancel: () => void; } export default function MapUserForm({ - externalUser, - providerId, - onSubmit, - onCancel, + externalUser, + providerId, + onSubmit, + onCancel }: Props) { - const [errorMessage, setErrorMessage] = useState(""); - const [usersToMap, setUsersToMap] = useState([]); - const [totalUnmapped, setTotalUnmapped] = useState([]); - const [displayUsers, setDisplayUsers] = useState([]); - const [currentPage, setCurrentPage] = useState(1); - const [selectedUser, setSelectedUser] = useState(null); - const usersPerPage = 5; + const [errorMessage, setErrorMessage] = useState(''); + const [usersToMap, setUsersToMap] = useState([]); + const [totalUnmapped, setTotalUnmapped] = useState([]); + const [displayUsers, setDisplayUsers] = useState([]); + const [currentPage, setCurrentPage] = useState(1); + const [selectedUser, setSelectedUser] = useState(null); + const usersPerPage = 5; - const handlePageChange = (newPage: number) => { - setCurrentPage(newPage); - }; + const handlePageChange = (newPage: number) => { + setCurrentPage(newPage); + }; - const handleUserSelection = (userId: number) => { - setSelectedUser(userId); - }; + const handleUserSelection = (userId: number) => { + setSelectedUser(userId); + }; - const handleSubmit = async (userId: number) => { - try { - setErrorMessage(""); - const response = await axios.post( - `/api/provider-platforms/${providerId}/map-user/${userId}`, - externalUser, - ); + const handleSubmit = async (userId: number) => { + try { + setErrorMessage(''); + const response = await axios.post( + `/api/provider-platforms/${providerId}/map-user/${userId}`, + externalUser + ); - if (response.status !== 201) { - onSubmit("", ToastState.error); - } - onSubmit( - "User created successfully with temporary password", - ToastState.success, - ); - } catch (error: any) { - setErrorMessage(error.response.data.message); - onSubmit("Failed to map user", ToastState.error); - return; - } - }; - - function handleGetAllUnmappedUsers() { - if (usersToMap.length === totalUnmapped.length) { - return; - } - if (displayUsers.length < totalUnmapped.length) { - totalUnmapped.length === 0 - ? onSubmit("No unmapped users in UnlockEd", ToastState.error) - : setDisplayUsers(totalUnmapped); - } else { - setDisplayUsers(usersToMap); - } - } + if (response.status !== 201) { + onSubmit('', ToastState.error); + } + onSubmit( + 'User created successfully with temporary password', + ToastState.success + ); + } catch (error: any) { + setErrorMessage(error.response.data.message); + onSubmit('Failed to map user', ToastState.error); + return; + } + }; - useEffect(() => { - async function fetchUsers() { - try { - const all = await axios.get( - `/api/users?include=only_unmapped&provider_id=${providerId}`, - ); - if (all.status !== 200) { - onSubmit("failed to fetch users, please try again", ToastState.error); - return; + function handleGetAllUnmappedUsers() { + if (usersToMap.length === totalUnmapped.length) { + return; } - setTotalUnmapped(all.data.data); - const response = await axios.get( - `/api/users?include=only_unmapped&provider_id=${providerId}&search=${externalUser.username}&search=${externalUser.email}`, - ); - if (response.status !== 200) { - onSubmit("Failed to fetch users", ToastState.error); - return; + if (displayUsers.length < totalUnmapped.length) { + totalUnmapped.length === 0 + ? onSubmit('No unmapped users in UnlockEd', ToastState.error) + : setDisplayUsers(totalUnmapped); + } else { + setDisplayUsers(usersToMap); } - setUsersToMap(response.data.data); - setDisplayUsers(response.data.data); - } catch (error: any) { - console.log(error); - setErrorMessage(error); - onSubmit("Failed to fetch users", ToastState.error); - return; - } } - externalUser && fetchUsers(); - }, [externalUser]); - const indexOfLastUser = currentPage * usersPerPage; - const indexOfFirstUser = indexOfLastUser - usersPerPage; - const currentUsers = displayUsers.slice(indexOfFirstUser, indexOfLastUser); - const totalPages = Math.ceil(displayUsers.length / usersPerPage); + useEffect(() => { + async function fetchUsers() { + try { + const all = await axios.get( + `/api/users?include=only_unmapped&provider_id=${providerId}` + ); + if (all.status !== 200) { + onSubmit( + 'failed to fetch users, please try again', + ToastState.error + ); + return; + } + setTotalUnmapped(all.data.data); + const response = await axios.get( + `/api/users?include=only_unmapped&provider_id=${providerId}&search=${externalUser.username}&search=${externalUser.email}` + ); + if (response.status !== 200) { + onSubmit('Failed to fetch users', ToastState.error); + return; + } + setUsersToMap(response.data.data); + setDisplayUsers(response.data.data); + } catch (error: any) { + console.log(error); + setErrorMessage(error); + onSubmit('Failed to fetch users', ToastState.error); + return; + } + } + externalUser && fetchUsers(); + }, [externalUser]); + + const indexOfLastUser = currentPage * usersPerPage; + const indexOfFirstUser = indexOfLastUser - usersPerPage; + const currentUsers = displayUsers.slice(indexOfFirstUser, indexOfLastUser); + const totalPages = Math.ceil(displayUsers.length / usersPerPage); - return ( -
    - -
    -
    Provider Username:
    -
    - {externalUser?.username ?? errorMessage} -
    + return ( +
    + +
    +
    Provider Username:
    +
    + {externalUser?.username ?? errorMessage} +
    +
    +
    Provider Email:
    +
    + {externalUser?.email ?? ''} +
    +
    to UnlockEd Users:
    +
    + +
    + {currentUsers.map((user) => ( +
    + handleUserSelection(user.id)} + /> + +
    + ))} +
    +
    + + +
    +
    +
    + +
    -
    Provider Email:
    -
    {externalUser?.email ?? ""}
    -
    to UnlockEd Users:
    -
    - -
    - {currentUsers.map((user) => ( -
    - handleUserSelection(user.id)} - /> - -
    - ))} -
    -
    - - -
    -
    -
    - -
    -
    - ); + ); } diff --git a/frontend/src/Components/forms/RegisterOidcClientForm.tsx b/frontend/src/Components/forms/RegisterOidcClientForm.tsx index 95dc7278..a5d817b1 100644 --- a/frontend/src/Components/forms/RegisterOidcClientForm.tsx +++ b/frontend/src/Components/forms/RegisterOidcClientForm.tsx @@ -1,90 +1,94 @@ -import { OidcClient, ProviderPlatform, ServerResponse } from "../../common"; -import axios from "axios"; -import { useState } from "react"; -import { useForm, SubmitHandler } from "react-hook-form"; -import { TextInput } from "../inputs/TextInput"; -import { SubmitButton } from "../inputs/SubmitButton"; -import { CloseX } from "../inputs/CloseX"; -import { ToastState } from "../Toast"; +import { OidcClient, ProviderPlatform, ServerResponse } from '../../common'; +import axios from 'axios'; +import { useState } from 'react'; +import { useForm, SubmitHandler } from 'react-hook-form'; +import { TextInput } from '../inputs/TextInput'; +import { SubmitButton } from '../inputs/SubmitButton'; +import { CloseX } from '../inputs/CloseX'; +import { ToastState } from '../Toast'; type Inputs = { - redirect_url: string; - provider_platform_id: number; - auto_register: boolean; + redirect_url: string; + provider_platform_id: number; + auto_register: boolean; }; export default function RegisterOidcClientForm({ - onSuccess, - provider, - onClose, + onSuccess, + provider, + onClose }: { - onSuccess: (response: ServerResponse, state: ToastState) => void; - provider: ProviderPlatform; - onClose: () => void; + onSuccess: ( + response: ServerResponse, + state: ToastState + ) => void; + provider: ProviderPlatform; + onClose: () => void; }) { - const [errorMessage, setErrorMessage] = useState(""); - const [hasAuto, setHasAuto] = useState(false); - const { - register, - handleSubmit, - formState: { errors }, - } = useForm({ - defaultValues: { - redirect_url: "", - provider_platform_id: provider.id, - }, - }); + const [errorMessage, setErrorMessage] = useState(''); + const [hasAuto, setHasAuto] = useState(false); + const { + register, + handleSubmit, + formState: { errors } + } = useForm({ + defaultValues: { + redirect_url: '', + provider_platform_id: provider.id + } + }); - const onSubmit: SubmitHandler = async (data) => { - try { - setErrorMessage(""); - data.auto_register = hasAuto; - const response = await axios.post("/api/oidc/clients", data); - if (response.status !== 201) { - setErrorMessage("Failed to register OIDC client."); - onSuccess(response.data, ToastState.error); - } - const client = response.data as ServerResponse; - onSuccess(client, ToastState.success); - } catch (error: any) { - setErrorMessage(error); - onSuccess(error.response.data, ToastState.error); - } - }; + const onSubmit: SubmitHandler = async (data) => { + try { + setErrorMessage(''); + data.auto_register = hasAuto; + const response = await axios.post('/api/oidc/clients', data); + if (response.status !== 201) { + setErrorMessage('Failed to register OIDC client.'); + onSuccess(response.data, ToastState.error); + } + const client = response.data as ServerResponse; + onSuccess(client, ToastState.success); + } catch (error: any) { + setErrorMessage(error); + onSuccess(error.response.data, ToastState.error); + } + }; - return ( - <> - onClose()} /> -
    - {!hasAuto && ( -
    - If you do not choose to auto register, you must manually setup - authentication for UnlockEd the provider platform's settings. -
    - )} - -
    - setHasAuto(!hasAuto)} - /> - - - - - - ); + return ( + <> + onClose()} /> +
    + {!hasAuto && ( +
    + If you do not choose to auto register, you must manually + setup authentication for UnlockEd the provider + platform's settings. +
    + )} + +
    + setHasAuto(!hasAuto)} + /> + + + + + + ); } diff --git a/frontend/src/Components/forms/ResetPasswordForm.tsx b/frontend/src/Components/forms/ResetPasswordForm.tsx index bea151a4..42169c8e 100644 --- a/frontend/src/Components/forms/ResetPasswordForm.tsx +++ b/frontend/src/Components/forms/ResetPasswordForm.tsx @@ -1,64 +1,68 @@ -import axios from "axios"; -import { User, UserRole } from "../../common"; -import { CloseX } from "../inputs/CloseX"; +import axios from 'axios'; +import { User, UserRole } from '../../common'; +import { CloseX } from '../inputs/CloseX'; interface ResetPasswordFormProps { - onCancel: (message: string, is_err: boolean) => void; - onSuccess: (psw: string) => void; - user: User | null; + onCancel: (message: string, is_err: boolean) => void; + onSuccess: (psw: string) => void; + user: User | null; } export default function ResetPasswordForm({ - user, - onSuccess, - onCancel, + user, + onSuccess, + onCancel }: ResetPasswordFormProps) { - const getTempPassword = async () => { - try { - const response = await axios.post("/api/users/student-password", { - user_id: user?.id, - }); - if (response.status !== 200) { - onCancel("Failed to reset password", true); - return; - } - console.log(response.data["temp_password"]); - onSuccess(response.data["temp_password"]); - return; - } catch (error: any) { - onCancel(error.response.data.message, true); - return; - } - }; - return ( -
    - onCancel("", false)} /> - {user?.role == UserRole.Admin ? ( -

    - You may only reset the password for non-administrator accounts. -

    - ) : ( + const getTempPassword = async () => { + try { + const response = await axios.post('/api/users/student-password', { + user_id: user?.id + }); + if (response.status !== 200) { + onCancel('Failed to reset password', true); + return; + } + console.log(response.data['temp_password']); + onSuccess(response.data['temp_password']); + return; + } catch (error: any) { + onCancel(error.response.data.message, true); + return; + } + }; + return (
    -

    - Are you sure you would like to reset {user?.name_first}{" "} - {user?.name_last}'s password? -

    -

    -
    - - -
    + onCancel('', false)} /> + {user?.role == UserRole.Admin ? ( +

    + You may only reset the password for non-administrator + accounts. +

    + ) : ( +
    +

    + Are you sure you would like to reset {user?.name_first}{' '} + {user?.name_last}'s password? +

    +

    +
    + + +
    +
    + )}
    - )} -
    - ); + ); } diff --git a/frontend/src/Components/forms/ShowImportedUsers.tsx b/frontend/src/Components/forms/ShowImportedUsers.tsx index 98a536c1..4f4bb09e 100644 --- a/frontend/src/Components/forms/ShowImportedUsers.tsx +++ b/frontend/src/Components/forms/ShowImportedUsers.tsx @@ -1,69 +1,73 @@ -import { useState } from "react"; -import { CloseX } from "../inputs"; -import { UserImports } from "@/common"; +import { useState } from 'react'; +import { CloseX } from '../inputs'; +import { UserImports } from '@/common'; interface ImportedUserProps { - users: UserImports[]; - onExit: () => void; + users: UserImports[]; + onExit: () => void; } const PaginatedUserTable = ({ users, onExit }: ImportedUserProps) => { - const [currentPage, setCurrentPage] = useState(1); - const usersPerPage = 5; - const indexOfLastUser = currentPage * usersPerPage; - const indexOfFirstUser = indexOfLastUser - usersPerPage; - const currentUsers = users.slice(indexOfFirstUser, indexOfLastUser); - const totalPages = Math.ceil(users.length / usersPerPage); + const [currentPage, setCurrentPage] = useState(1); + const usersPerPage = 5; + const indexOfLastUser = currentPage * usersPerPage; + const indexOfFirstUser = indexOfLastUser - usersPerPage; + const currentUsers = users.slice(indexOfFirstUser, indexOfLastUser); + const totalPages = Math.ceil(users.length / usersPerPage); - const handlePageChange = (newPage: number) => { - setCurrentPage(newPage); - }; + const handlePageChange = (newPage: number) => { + setCurrentPage(newPage); + }; - return ( -
    - -
    Imported Users
    -
    - - - - - - - - - - {currentUsers.map((user, idx) => ( - - - - - - ))} - -
    UsernameTemp PasswordError
    {user.username}{user.temp_password ?? "n/a"}{user.error ?? "n/a"}
    -
    -
    - - - Page {currentPage} of {totalPages} - - -
    -
    - ); + return ( +
    + +
    Imported Users
    +
    + + + + + + + + + + {currentUsers.map((user, idx) => ( + + + + + + ))} + +
    UsernameTemp PasswordError
    {user.username} + {user.temp_password ?? 'n/a'} + + {user.error ?? 'n/a'} +
    +
    +
    + + + Page {currentPage} of {totalPages} + + +
    +
    + ); }; export default PaginatedUserTable; diff --git a/frontend/src/Components/forms/ShowTempPasswordForm.tsx b/frontend/src/Components/forms/ShowTempPasswordForm.tsx index 5c5db8aa..1d5165ce 100644 --- a/frontend/src/Components/forms/ShowTempPasswordForm.tsx +++ b/frontend/src/Components/forms/ShowTempPasswordForm.tsx @@ -1,40 +1,42 @@ -import { CloseX } from "../inputs"; +import { CloseX } from '../inputs'; export interface TempPasswordProps { - tempPassword: string; - userName: string | null; - onClose: () => void; + tempPassword: string; + userName: string | null; + onClose: () => void; } export default function ShowTempPasswordForm({ - tempPassword, - userName, - onClose, + tempPassword, + userName, + onClose }: TempPasswordProps) { - return ( -
    - onClose()} /> - {userName == null ? ( -

    You have successfully created a new user.

    - ) : ( - <> -

    - You have successfully reset {userName}'s password. -

    -

    - Copy this password now. If you lose it, you'll need to regenerate it - to get a new one. -

    - - )} -
    -
    -
    -
    Temporary Password
    -
    {tempPassword}
    -
    + return ( +
    + onClose()} /> + {userName == null ? ( +

    + You have successfully created a new user. +

    + ) : ( + <> +

    + You have successfully reset {userName}'s password. +

    +

    + Copy this password now. If you lose it, you'll need to + regenerate it to get a new one. +

    + + )} +
    +
    +
    +
    Temporary Password
    +
    {tempPassword}
    +
    +
    +
    +

    -
    -

    -
    - ); + ); } diff --git a/frontend/src/Components/inputs/Checkbox.tsx b/frontend/src/Components/inputs/Checkbox.tsx index 0133c474..c88fe95c 100644 --- a/frontend/src/Components/inputs/Checkbox.tsx +++ b/frontend/src/Components/inputs/Checkbox.tsx @@ -1,24 +1,24 @@ interface CheckboxProps { - label: string; - interfaceRef: string; - register: Function; + label: string; + interfaceRef: string; + register: Function; } export default function Checkbox({ - label, - interfaceRef, - register, + label, + interfaceRef, + register }: CheckboxProps) { - return ( -
    - -
    - ); + return ( +
    + +
    + ); } diff --git a/frontend/src/Components/inputs/CloseX.tsx b/frontend/src/Components/inputs/CloseX.tsx index 24b8ddbf..e8fa26f2 100644 --- a/frontend/src/Components/inputs/CloseX.tsx +++ b/frontend/src/Components/inputs/CloseX.tsx @@ -1,12 +1,12 @@ export function CloseX({ close }: { close: () => void }) { - return ( -
    - -
    - ); + return ( +
    + +
    + ); } diff --git a/frontend/src/Components/inputs/DropdownControl.tsx b/frontend/src/Components/inputs/DropdownControl.tsx index d8349228..f8943314 100644 --- a/frontend/src/Components/inputs/DropdownControl.tsx +++ b/frontend/src/Components/inputs/DropdownControl.tsx @@ -1,34 +1,34 @@ interface DropdownControlProps { - label?: string; - callback: Function; - enumType: Record; + label?: string; + callback: Function; + enumType: Record; } /* a dropdown that executes a callback function on change */ export default function DropdownControl({ - label, - callback, - enumType, + label, + callback, + enumType }: DropdownControlProps) { - return ( - - ); + return ( + + ); } diff --git a/frontend/src/Components/inputs/DropdownInput.tsx b/frontend/src/Components/inputs/DropdownInput.tsx index fa16ca64..d77a9fac 100644 --- a/frontend/src/Components/inputs/DropdownInput.tsx +++ b/frontend/src/Components/inputs/DropdownInput.tsx @@ -1,45 +1,45 @@ -import { FieldErrors } from "react-hook-form"; +import { FieldErrors } from 'react-hook-form'; interface DropdownProps { - label: string; - interfaceRef: string; - required: boolean; - errors: FieldErrors; - register: Function; - enumType: Record; + label: string; + interfaceRef: string; + required: boolean; + errors: FieldErrors; + register: Function; + enumType: Record; } export function DropdownInput({ - label, - interfaceRef, - required, - errors, - register, - enumType, + label, + interfaceRef, + required, + errors, + register, + enumType }: DropdownProps) { - return ( - - ); + return ( + + ); } diff --git a/frontend/src/Components/inputs/InputError.tsx b/frontend/src/Components/inputs/InputError.tsx index 8e344162..34db8c6f 100644 --- a/frontend/src/Components/inputs/InputError.tsx +++ b/frontend/src/Components/inputs/InputError.tsx @@ -1,16 +1,16 @@ -import { HTMLAttributes } from "react"; +import { HTMLAttributes } from 'react'; export default function InputError({ - message, - className = "", - ...props + message, + className = '', + ...props }: HTMLAttributes & { message?: string }) { - return message ? ( -
    - {message} -
    - ) : null; + return message ? ( +
    + {message} +
    + ) : null; } diff --git a/frontend/src/Components/inputs/SearchBar.tsx b/frontend/src/Components/inputs/SearchBar.tsx index dc2b3d66..7393e980 100644 --- a/frontend/src/Components/inputs/SearchBar.tsx +++ b/frontend/src/Components/inputs/SearchBar.tsx @@ -1,25 +1,25 @@ -import { MagnifyingGlassIcon } from "@heroicons/react/24/solid"; +import { MagnifyingGlassIcon } from '@heroicons/react/24/solid'; export default function SearchBar({ - searchTerm, - changeCallback, + searchTerm, + changeCallback }: { - searchTerm: string; - changeCallback: (arg: string) => void; + searchTerm: string; + changeCallback: (arg: string) => void; }) { - return ( - - ); + return ( + + ); } diff --git a/frontend/src/Components/inputs/SubmitButton.tsx b/frontend/src/Components/inputs/SubmitButton.tsx index aea2ea21..18b4297e 100644 --- a/frontend/src/Components/inputs/SubmitButton.tsx +++ b/frontend/src/Components/inputs/SubmitButton.tsx @@ -1,8 +1,8 @@ export function SubmitButton({ errorMessage }: { errorMessage: string }) { - return ( - - ); + return ( + + ); } diff --git a/frontend/src/Components/inputs/TextAreaInput.tsx b/frontend/src/Components/inputs/TextAreaInput.tsx index b903779f..e21b149e 100644 --- a/frontend/src/Components/inputs/TextAreaInput.tsx +++ b/frontend/src/Components/inputs/TextAreaInput.tsx @@ -1,46 +1,46 @@ -import { FieldErrors } from "react-hook-form"; +import { FieldErrors } from 'react-hook-form'; interface TextAreaProps { - label: string; - interfaceRef: string; - required: boolean; - length: number | null; - errors: FieldErrors; - register: Function; + label: string; + interfaceRef: string; + required: boolean; + length: number | null; + errors: FieldErrors; + register: Function; } export function TextAreaInput({ - label, - interfaceRef, - required, - length, - errors, - register, + label, + interfaceRef, + required, + length, + errors, + register }: TextAreaProps) { - const options = { - required: { - value: required, - message: `${label} is required`, - }, - ...(length !== null && { - maxLength: { - value: length, - message: `${label} should be ${length} characters or less`, - }, - }), - }; - return ( -