From 401fbb58e314b7a117bda1266569d9a19dba1cb8 Mon Sep 17 00:00:00 2001 From: Simon Larsen Date: Tue, 18 Jun 2024 10:38:45 +0100 Subject: [PATCH] refactor: Update import statements for TimezoneUtil in multiple files --- Accounts/src/Pages/LoginWithSSO.tsx | 33 +++++++-------- App/FeatureSet/BaseAPI/Index.ts | 13 +++--- .../Home/Static/img/skillable-logo.svg | 1 + App/FeatureSet/Home/Views/logo-roll.ejs | 2 +- CommonServer/API/UserAPI.ts | 11 ----- CommonUI/package-lock.json | 9 +++++ CommonUI/package.json | 1 + CommonUI/src/Components/Forms/BasicForm.tsx | 1 + CommonUI/src/Components/Forms/Types/Field.ts | 1 + CommonUI/src/Utils/JWT.ts | 9 +++++ CommonUI/src/Utils/User.ts | 26 ++++++++++++ Dashboard/src/App.tsx | 15 +++++++ .../Monitor/MonitorCriteriaIncidentForm.tsx | 13 +++++- Dashboard/src/Pages/Incidents/View/Index.tsx | 40 +++++++++++++++++++ Model/Models/Incident.ts | 37 +++++++++++++++++ 15 files changed, 178 insertions(+), 34 deletions(-) create mode 100644 App/FeatureSet/Home/Static/img/skillable-logo.svg delete mode 100644 CommonServer/API/UserAPI.ts create mode 100644 CommonUI/src/Utils/JWT.ts diff --git a/Accounts/src/Pages/LoginWithSSO.tsx b/Accounts/src/Pages/LoginWithSSO.tsx index 86d9918a4a..0017dc4bfd 100644 --- a/Accounts/src/Pages/LoginWithSSO.tsx +++ b/Accounts/src/Pages/LoginWithSSO.tsx @@ -11,7 +11,6 @@ import UserUtil from "CommonUI/src/Utils/User"; import User from "Model/Models/User"; import React, { useState } from "react"; import ProjectSSO from "Model/Models/ProjectSSO"; -import ErrorMessage from "CommonUI/src/Components/ErrorMessage/ErrorMessage"; import PageLoader from "CommonUI/src/Components/Loader/PageLoader"; import API from "CommonUI/src/Utils/API/API"; import BasicForm from "CommonUI/src/Components/Forms/BasicForm"; @@ -44,25 +43,29 @@ const LoginPage: () => JSX.Element = () => { } if (!listResult.data || (listResult.data as JSONArray).length === 0) { - return setError("No SSO configuration found for the email: " + email.toString()); + setError("No SSO configuration found for the email: " + email.toString()); + } else { + setProjectSsoConfigList(ProjectSSO.fromJSONArray(listResult['data'], ProjectSSO)); } - setProjectSsoConfigList(ProjectSSO.fromJSONArray(listResult['data'], ProjectSSO)); - } catch (error) { setError(API.getFriendlyErrorMessage(error as Error)); } - setIsLoading(false); } else { setError("Email is required to perform this action"); } + + setIsLoading(false); + }; const getSsoConfigModelList = (configs: Array) => { - return ( + return ( + + list={configs} titleField="name" selectedItems={[]} @@ -81,25 +84,21 @@ const LoginPage: () => JSX.Element = () => { />); } - - if (error) { - return ; - } - if (isLoading) { return ; } const getProjectName = (projectId: string): string => { - const projectNames = projectSsoConfigList.filter((config: ProjectSSO) => config.projectId?.toString() === projectId.toString()).map((config: ProjectSSO) => config.project?.name); + const projectNames = projectSsoConfigList.filter((config: ProjectSSO) => config.projectId?.toString() === projectId.toString()).map((config: ProjectSSO) => config.project?.name); return projectNames[0] || 'Project'; } - if (projectSsoConfigList.length > 0) { + if (projectSsoConfigList.length > 0 && !error && !isLoading) { const projectIds: Array = projectSsoConfigList.map((config: ProjectSSO) => config.projectId?.toString() as string); return ( +
JSX.Element = () => { src={OneUptimeLogo} alt="OneUptime" /> -

+

Select Project

@@ -118,14 +117,15 @@ const LoginPage: () => JSX.Element = () => { {projectIds.map((projectId: string) => { return (

-

- Project: {getProjectName(projectId)} +

+ {getProjectName(projectId)}

{getSsoConfigModelList(projectSsoConfigList.filter((config: ProjectSSO) => config.projectId?.toString() === projectId.toString()))}
) })}
+
); } @@ -154,6 +154,7 @@ const LoginPage: () => JSX.Element = () => { (User, UserService).getRouter(), - ); + app.use( `/${APP_NAME.toLocaleLowerCase()}`, @@ -1211,6 +1207,13 @@ const BaseAPIFeatureSet: FeatureSet = { ); app.use(`/${APP_NAME.toLocaleLowerCase()}`, NotificationAPI); + + + //attach api's + app.use( + `/${APP_NAME.toLocaleLowerCase()}`, + new BaseAPI(User, UserService).getRouter(), + ); }, }; diff --git a/App/FeatureSet/Home/Static/img/skillable-logo.svg b/App/FeatureSet/Home/Static/img/skillable-logo.svg new file mode 100644 index 0000000000..2d3f95a33f --- /dev/null +++ b/App/FeatureSet/Home/Static/img/skillable-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/App/FeatureSet/Home/Views/logo-roll.ejs b/App/FeatureSet/Home/Views/logo-roll.ejs index f3d8004115..092dd02462 100644 --- a/App/FeatureSet/Home/Views/logo-roll.ejs +++ b/App/FeatureSet/Home/Views/logo-roll.ejs @@ -11,7 +11,7 @@ ViewSonic
- Siemens + Skillable
Sodexo diff --git a/CommonServer/API/UserAPI.ts b/CommonServer/API/UserAPI.ts deleted file mode 100644 index ffef8f1377..0000000000 --- a/CommonServer/API/UserAPI.ts +++ /dev/null @@ -1,11 +0,0 @@ -import UserService, { - Service as UserServiceType, -} from "../Services/UserService"; -import BaseAPI from "./BaseAPI"; -import User from "Model/Models/User"; - -export default class UserAPI extends BaseAPI { - public constructor() { - super(User, UserService); - } -} diff --git a/CommonUI/package-lock.json b/CommonUI/package-lock.json index 3bbbc23040..cbc7b05072 100644 --- a/CommonUI/package-lock.json +++ b/CommonUI/package-lock.json @@ -27,6 +27,7 @@ "Common": "file:../Common", "formik": "^2.4.6", "history": "^5.3.0", + "jwt-decode": "^4.0.0", "lodash": "^4.17.21", "Model": "file:../Model", "moment-timezone": "^0.5.45", @@ -14543,6 +14544,14 @@ "node": ">=6" } }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "engines": { + "node": ">=18" + } + }, "node_modules/kleur": { "version": "3.0.3", "dev": true, diff --git a/CommonUI/package.json b/CommonUI/package.json index 3f115633e3..8c80759cad 100644 --- a/CommonUI/package.json +++ b/CommonUI/package.json @@ -32,6 +32,7 @@ "Common": "file:../Common", "formik": "^2.4.6", "history": "^5.3.0", + "jwt-decode": "^4.0.0", "lodash": "^4.17.21", "Model": "file:../Model", "moment-timezone": "^0.5.45", diff --git a/CommonUI/src/Components/Forms/BasicForm.tsx b/CommonUI/src/Components/Forms/BasicForm.tsx index 366e3f5595..aef2621435 100644 --- a/CommonUI/src/Components/Forms/BasicForm.tsx +++ b/CommonUI/src/Components/Forms/BasicForm.tsx @@ -121,6 +121,7 @@ const BasicForm: ForwardRefExoticComponent = forwardRef( null, ); + useEffect(() => { if (formSteps && formSteps.length > 0 && formSteps[0]) { setCurrentFormStepId(formSteps[0].id); diff --git a/CommonUI/src/Components/Forms/Types/Field.ts b/CommonUI/src/Components/Forms/Types/Field.ts index 0b41c71002..52d89618e8 100644 --- a/CommonUI/src/Components/Forms/Types/Field.ts +++ b/CommonUI/src/Components/Forms/Types/Field.ts @@ -97,4 +97,5 @@ export default interface Field { // set this to true if you want to show this field in the form even when the form is in edit mode. doNotShowWhenEditing?: boolean | undefined; doNotShowWhenCreating?: boolean | undefined; + } diff --git a/CommonUI/src/Utils/JWT.ts b/CommonUI/src/Utils/JWT.ts new file mode 100644 index 0000000000..987bc99817 --- /dev/null +++ b/CommonUI/src/Utils/JWT.ts @@ -0,0 +1,9 @@ + +import { JSONObject } from "Common/Types/JSON"; +import { jwtDecode } from "jwt-decode"; + +export default class JWTToken { + public static decodeToken(token: string): JSONObject { + return jwtDecode(token); + } +} \ No newline at end of file diff --git a/CommonUI/src/Utils/User.ts b/CommonUI/src/Utils/User.ts index 5a42b1ae1c..1a2589610c 100644 --- a/CommonUI/src/Utils/User.ts +++ b/CommonUI/src/Utils/User.ts @@ -9,6 +9,7 @@ import Name from "Common/Types/Name"; import ObjectID from "Common/Types/ObjectID"; import Timezone from "Common/Types/Timezone"; import API from "Common/Utils/API"; +import JWTToken from "./JWT"; export default class User { public static setProfilePicId(id: ObjectID | null): void { @@ -62,6 +63,31 @@ export default class User { LocalStorage.setItem("user_name", name.toString()); } + public static refreshUserDataFromToken(token: string): void { + const decodedToken: JSONObject = JWTToken.decodeToken(token); + + if(decodedToken["userId"]) { + this.setUserId(new ObjectID(decodedToken["userId"] as string)); + } + + if(decodedToken["email"]) { + this.setEmail(new Email(decodedToken["email"] as string)); + } + + if(decodedToken["name"]) { + this.setName(new Name(decodedToken["name"] as string)); + } + + if(decodedToken["isMasterAdmin"]) { + this.setIsMasterAdmin(decodedToken["isMasterAdmin"] as boolean); + } + + if(decodedToken["timezone"]) { + this.setSavedUserTimezone(decodedToken["timezone"] as Timezone); + } + + } + public static getEmail(): Email | null { if (!LocalStorage.getItem("user_email")) { return null; diff --git a/Dashboard/src/App.tsx b/Dashboard/src/App.tsx index 08d36ca59b..6d117f4e78 100644 --- a/Dashboard/src/App.tsx +++ b/Dashboard/src/App.tsx @@ -54,6 +54,9 @@ import { useParams, } from "react-router-dom"; import useAsyncEffect from "use-async-effect"; +import User from "CommonUI/src/Utils/User"; +import Cookie from "CommonUI/src/Utils/Cookie"; +import { JSONValue } from "Common/Types/JSON"; const App: () => JSX.Element = () => { Navigation.setNavigateHook(useNavigate()); @@ -75,6 +78,18 @@ const App: () => JSX.Element = () => { const [hasPaymentMethod, setHasPaymentMethod] = useState(false); + + useEffect(() => { + const token: JSONValue = Cookie.getItem("user_token"); + + if(!token) { + return; + } + + User.refreshUserDataFromToken(token.toString()); + }, []); + + useAsyncEffect(async () => { try { if (selectedProject && selectedProject._id) { diff --git a/Dashboard/src/Components/Form/Monitor/MonitorCriteriaIncidentForm.tsx b/Dashboard/src/Components/Form/Monitor/MonitorCriteriaIncidentForm.tsx index c2b4d8aa34..9d4e56e37e 100644 --- a/Dashboard/src/Components/Form/Monitor/MonitorCriteriaIncidentForm.tsx +++ b/Dashboard/src/Components/Form/Monitor/MonitorCriteriaIncidentForm.tsx @@ -47,7 +47,7 @@ const MonitorCriteriaIncidentForm: FunctionComponent = ( }, title: "Incident Description", stepId: "incident-details", - fieldType: FormFieldSchemaType.LongText, + fieldType: FormFieldSchemaType.Markdown, required: true, placeholder: "Description", }, @@ -88,6 +88,17 @@ const MonitorCriteriaIncidentForm: FunctionComponent = ( fieldType: FormFieldSchemaType.Toggle, required: false, }, + { + field: { + remediationNotes: true, + }, + title: "Remediation Notes", + stepId: "incident-details", + description: + "Notes to help the on-call engineer resolve this incident.", + fieldType: FormFieldSchemaType.Markdown, + required: false, + }, ]} /> diff --git a/Dashboard/src/Pages/Incidents/View/Index.tsx b/Dashboard/src/Pages/Incidents/View/Index.tsx index af74f3b8da..5190e5bde0 100644 --- a/Dashboard/src/Pages/Incidents/View/Index.tsx +++ b/Dashboard/src/Pages/Incidents/View/Index.tsx @@ -589,6 +589,46 @@ const IncidentView: FunctionComponent< modelId: modelId, }} /> + + + ); }; diff --git a/Model/Models/Incident.ts b/Model/Models/Incident.ts index 0dc1b52efe..42980c9aec 100644 --- a/Model/Models/Incident.ts +++ b/Model/Models/Incident.ts @@ -1015,4 +1015,41 @@ export default class Incident extends BaseModel { default: false, }) public isCreatedAutomatically?: boolean = undefined; + + + + @ColumnAccessControl({ + create: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.CreateProjectIncident, + ], + read: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.ReadProjectIncident, + ], + update: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.EditProjectIncident, + ], + }) + @TableColumn({ + required: false, + type: TableColumnType.Markdown, + title: "Remediation Notes", + description: + "Notes on how to remediate this incident. This is in markdown.", + }) + @Column({ + nullable: true, + type: ColumnType.Markdown, + }) + public remediationNotes?: string = undefined; + + }