feat: Add support for displaying Open Source Friends

This commit is contained in:
Simon Larsen 2024-07-30 10:50:26 -06:00
parent 2b1ad303d9
commit 1cd89851bb
No known key found for this signature in database
GPG Key ID: 96C5DCA24769DBCA
12 changed files with 478 additions and 91 deletions

View File

@ -23,6 +23,10 @@ import {
useNavigate,
useParams,
} from "react-router-dom";
import UserView from "./Pages/Users/View/Index";
import UserDelete from "./Pages/Users/View/Delete";
import ProjectView from "./Pages/Projects/View/Index";
import ProjectDelete from "./Pages/Projects/View/Delete";
const App: () => JSX.Element = () => {
Navigation.setNavigateHook(useNavigate());
@ -61,6 +65,26 @@ const App: () => JSX.Element = () => {
element={<Users />}
/>
<PageRoute
path={RouteMap[PageMap.USER_VIEW]?.toString() || ""}
element={<UserView />}
/>
<PageRoute
path={RouteMap[PageMap.USER_DELETE]?.toString() || ""}
element={<UserDelete />}
/>
<PageRoute
path={RouteMap[PageMap.PROJECT_VIEW]?.toString() || ""}
element={<ProjectView />}
/>
<PageRoute
path={RouteMap[PageMap.PROJECT_DELETE]?.toString() || ""}
element={<ProjectDelete />}
/>
<PageRoute
path={RouteMap[PageMap.LOGOUT]?.toString() || ""}
element={<Logout />}

View File

@ -187,7 +187,7 @@ const Projects: FunctionComponent = (): ReactElement => {
isEditable={false}
isCreateable={true}
name="Projects"
isViewable={false}
isViewable={true}
cardProps={{
title: "Projects",
description: "Here is a list of proejcts in OneUptime.",

View File

@ -0,0 +1,52 @@
import PageMap from "../../../Utils/PageMap";
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import ObjectID from "Common/Types/ObjectID";
import ModelDelete from "CommonUI/src/Components/ModelDelete/ModelDelete";
import Navigation from "CommonUI/src/Utils/Navigation";
import React, { FunctionComponent, ReactElement } from "react";
import SideMenuComponent from "./SideMenu";
import Project from "Model/Models/Project";
import ModelPage from "CommonUI/src/Components/Page/ModelPage";
const DeletePage: FunctionComponent = (): ReactElement => {
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
return (
<ModelPage<Project>
modelId={modelId}
modelNameField="name"
modelType={Project}
title={"Project"}
breadcrumbLinks={[
{
title: "Admin Dashboard",
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
},
{
title: "Projects",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.PROJECTS] as Route,
),
},
{
title: "Project",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.PROJECT_VIEW] as Route,
),
},
]}
sideMenu={<SideMenuComponent modelId={modelId} />}
>
<ModelDelete
modelType={Project}
modelId={modelId}
onDeleteSuccess={() => {
Navigation.navigate(RouteMap[PageMap.PROJECTS] as Route);
}}
/>
</ModelPage>
);
};
export default DeletePage;

View File

@ -0,0 +1,90 @@
import ObjectID from "Common/Types/ObjectID";
import PageMap from "../../../Utils/PageMap";
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import Navigation from "CommonUI/src/Utils/Navigation";
import Project from "Model/Models/Project";
import React, { FunctionComponent, ReactElement } from "react";
import CardModelDetail from "CommonUI/src/Components/ModelDetail/CardModelDetail";
import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType";
import FieldType from "CommonUI/src/Components/Types/FieldType";
import ModelPage from "CommonUI/src/Components/Page/ModelPage";
import SideMenuComponent from "./SideMenu";
const Projects: FunctionComponent = (): ReactElement => {
const modelId: ObjectID = Navigation.getLastParamAsObjectID();
return (
<ModelPage
modelId={modelId}
modelNameField="name"
modelType={Project}
title={"Project"}
breadcrumbLinks={[
{
title: "Admin Dashboard",
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
},
{
title: "Projects",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.PROJECTS] as Route,
),
},
{
title: "Project",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.PROJECT_VIEW] as Route,
),
},
]}
sideMenu={<SideMenuComponent modelId={modelId} />}
>
<div>
<CardModelDetail<Project>
name="Project"
cardProps={{
title: "Project",
description: "Project details",
}}
isEditable={true}
editButtonText="Edit Project"
formFields={[
{
field: {
name: true,
},
title: "Name",
fieldType: FormFieldSchemaType.Text,
required: true,
},
]}
modelDetailProps={{
modelType: Project,
id: "model-detail-user",
fields: [
{
field: {
_id: true,
},
title: "Project ID",
fieldType: FieldType.Text,
placeholder: "-",
},
{
field: {
name: true,
},
title: "Name",
fieldType: FieldType.Text,
},
],
modelId: modelId,
}}
/>
</div>
</ModelPage>
);
};
export default Projects;

View File

@ -0,0 +1,53 @@
import PageMap from "../../../Utils/PageMap";
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import IconProp from "Common/Types/Icon/IconProp";
import ObjectID from "Common/Types/ObjectID";
import SideMenu from "CommonUI/src/Components/SideMenu/SideMenu";
import SideMenuItem from "CommonUI/src/Components/SideMenu/SideMenuItem";
import SideMenuSection from "CommonUI/src/Components/SideMenu/SideMenuSection";
import React, { FunctionComponent, ReactElement } from "react";
export interface SideMenuProps {
modelId: ObjectID;
}
const SideMenuComponent: FunctionComponent<SideMenuProps> = (
props: SideMenuProps,
): ReactElement => {
return (
<SideMenu>
<SideMenuSection title="Basic">
<SideMenuItem
link={{
title: "Overview",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.PROJECT_VIEW] as Route,
{
modelId: props.modelId,
},
),
}}
icon={IconProp.Info}
/>
</SideMenuSection>
<SideMenuSection title="Advanced">
<SideMenuItem
link={{
title: "Delete",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.PROJECT_DELETE] as Route,
{
modelId: props.modelId,
},
),
}}
icon={IconProp.Trash}
/>
</SideMenuSection>
</SideMenu>
);
};
export default SideMenuComponent;

View File

@ -1,31 +1,15 @@
import PageMap from "../../Utils/PageMap";
import RouteMap, { RouteUtil } from "../../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import { ErrorFunction } from "Common/Types/FunctionTypes";
import { ButtonStyleType } from "CommonUI/src/Components/Button/Button";
import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType";
import ConfirmModal from "CommonUI/src/Components/Modal/ConfirmModal";
import ModelTable from "CommonUI/src/Components/ModelTable/ModelTable";
import Page from "CommonUI/src/Components/Page/Page";
import FieldType from "CommonUI/src/Components/Types/FieldType";
import API from "CommonUI/src/Utils/API/API";
import ModelAPI from "CommonUI/src/Utils/ModelAPI/ModelAPI";
import Navigation from "CommonUI/src/Utils/Navigation";
import User from "Model/Models/User";
import React, { FunctionComponent, ReactElement, useState } from "react";
import React, { FunctionComponent, ReactElement } from "react";
const Users: FunctionComponent = (): ReactElement => {
const [showConfirmVerifyEmailModal, setShowConfirmVerifyEmailModal] =
useState<boolean>(false);
const [selectedUser, setSelectedUser] = useState<User | null>(null);
const [error, setError] = useState<string | null>(null);
const [isConfimModalLoading, setIsConfirmModalLoading] =
useState<boolean>(false);
const [refreshItemsTrigger, setRefreshItemsTrigger] =
useState<boolean>(false);
return (
<Page
title={"Users"}
@ -46,38 +30,13 @@ const Users: FunctionComponent = (): ReactElement => {
isDeleteable={false}
isEditable={false}
showViewIdButton={true}
refreshToggle={refreshItemsTrigger}
isCreateable={true}
name="Users"
isViewable={false}
isViewable={true}
cardProps={{
title: "Users",
description: "Here is a list of users in OneUptime.",
}}
actionButtons={[
{
title: "Verify Email",
buttonStyleType: ButtonStyleType.NORMAL,
isVisible: (item: User) => {
return !item.isEmailVerified;
},
onClick: async (
item: User,
onCompleteAction: VoidFunction,
onError: ErrorFunction,
) => {
try {
setSelectedUser(item);
setShowConfirmVerifyEmailModal(true);
onCompleteAction();
} catch (err) {
onCompleteAction();
onError(err as Error);
}
},
},
]}
noItemsMessage={"No users found."}
formFields={[
{
@ -164,53 +123,6 @@ const Users: FunctionComponent = (): ReactElement => {
},
]}
/>
{error ? (
<ConfirmModal
title={`Error`}
description={error}
submitButtonText={"Close"}
onSubmit={async () => {
setError(null);
}}
submitButtonType={ButtonStyleType.NORMAL}
/>
) : (
<></>
)}
{showConfirmVerifyEmailModal && selectedUser ? (
<ConfirmModal
title={`Verify Email`}
description={`Are you sure you want to verify the email - ${selectedUser.email}?`}
isLoading={isConfimModalLoading}
submitButtonText={"Verify Email"}
onClose={async () => {
setShowConfirmVerifyEmailModal(false);
setSelectedUser(null);
}}
onSubmit={async () => {
try {
setIsConfirmModalLoading(true);
await ModelAPI.updateById<User>({
modelType: User,
id: selectedUser.id!,
data: {
isEmailVerified: true,
},
});
} catch (err) {
setError(API.getFriendlyMessage(err as Error));
}
setRefreshItemsTrigger(!refreshItemsTrigger);
setIsConfirmModalLoading(false);
setShowConfirmVerifyEmailModal(false);
}}
/>
) : (
<></>
)}
</Page>
);
};

View File

@ -0,0 +1,50 @@
import PageMap from "../../../Utils/PageMap";
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import ObjectID from "Common/Types/ObjectID";
import ModelDelete from "CommonUI/src/Components/ModelDelete/ModelDelete";
import Navigation from "CommonUI/src/Utils/Navigation";
import React, { FunctionComponent, ReactElement } from "react";
import SideMenuComponent from "./SideMenu";
import User from "Model/Models/User";
import ModelPage from "CommonUI/src/Components/Page/ModelPage";
const DeletePage: FunctionComponent = (): ReactElement => {
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
return (
<ModelPage
modelId={modelId}
modelNameField="email"
modelType={User}
title={"User"}
breadcrumbLinks={[
{
title: "Admin Dashboard",
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
},
{
title: "Users",
to: RouteUtil.populateRouteParams(RouteMap[PageMap.USERS] as Route),
},
{
title: "User",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.USER_VIEW] as Route,
),
},
]}
sideMenu={<SideMenuComponent modelId={modelId} />}
>
<ModelDelete
modelType={User}
modelId={modelId}
onDeleteSuccess={() => {
Navigation.navigate(RouteMap[PageMap.USERS] as Route);
}}
/>
</ModelPage>
);
};
export default DeletePage;

View File

@ -0,0 +1,136 @@
import ObjectID from "Common/Types/ObjectID";
import PageMap from "../../../Utils/PageMap";
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import Navigation from "CommonUI/src/Utils/Navigation";
import User from "Model/Models/User";
import React, { FunctionComponent, ReactElement } from "react";
import CardModelDetail from "CommonUI/src/Components/ModelDetail/CardModelDetail";
import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType";
import FieldType from "CommonUI/src/Components/Types/FieldType";
import ModelPage from "CommonUI/src/Components/Page/ModelPage";
import SideMenuComponent from "./SideMenu";
const Users: FunctionComponent = (): ReactElement => {
const modelId: ObjectID = Navigation.getLastParamAsObjectID();
return (
<ModelPage
modelId={modelId}
modelNameField="email"
modelType={User}
title={"User"}
breadcrumbLinks={[
{
title: "Admin Dashboard",
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
},
{
title: "Users",
to: RouteUtil.populateRouteParams(RouteMap[PageMap.USERS] as Route),
},
{
title: "User",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.USER_VIEW] as Route,
),
},
]}
sideMenu={<SideMenuComponent modelId={modelId} />}
>
<div>
<CardModelDetail<User>
name="User"
cardProps={{
title: "User",
description: "User details",
}}
isEditable={true}
editButtonText="Edit User"
formFields={[
{
field: {
name: true,
},
title: "Name",
fieldType: FormFieldSchemaType.Text,
required: true,
},
{
field: {
email: true,
},
title: "Email",
fieldType: FormFieldSchemaType.Email,
required: true,
},
{
field: {
isEmailVerified: true,
},
title: "Email Verified",
fieldType: FormFieldSchemaType.Toggle,
required: false,
},
{
field: {
enableTwoFactorAuth: true,
},
title: "Two Factor Auth Enabled",
fieldType: FormFieldSchemaType.Toggle,
required: false,
},
]}
modelDetailProps={{
modelType: User,
id: "model-detail-user",
fields: [
{
field: {
_id: true,
},
title: "User ID",
fieldType: FieldType.Text,
placeholder: "-",
},
{
field: {
name: true,
},
title: "Name",
fieldType: FieldType.Text,
},
{
field: {
email: true,
},
title: "Email",
fieldType: FieldType.Email,
placeholder: "-",
},
{
field: {
isEmailVerified: true,
},
title: "Email Verified",
fieldType: FieldType.Boolean,
placeholder: "No",
},
{
field: {
enableTwoFactorAuth: true,
},
title: "Two Factor Auth Enabled",
fieldType: FieldType.Boolean,
placeholder: "No",
},
],
modelId: modelId,
}}
/>
</div>
</ModelPage>
);
};
export default Users;

View File

@ -0,0 +1,53 @@
import PageMap from "../../../Utils/PageMap";
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
import Route from "Common/Types/API/Route";
import IconProp from "Common/Types/Icon/IconProp";
import ObjectID from "Common/Types/ObjectID";
import SideMenu from "CommonUI/src/Components/SideMenu/SideMenu";
import SideMenuItem from "CommonUI/src/Components/SideMenu/SideMenuItem";
import SideMenuSection from "CommonUI/src/Components/SideMenu/SideMenuSection";
import React, { FunctionComponent, ReactElement } from "react";
export interface SideMenuProps {
modelId: ObjectID;
}
const SideMenuComponent: FunctionComponent<SideMenuProps> = (
props: SideMenuProps,
): ReactElement => {
return (
<SideMenu>
<SideMenuSection title="Basic">
<SideMenuItem
link={{
title: "Overview",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.USER_VIEW] as Route,
{
modelId: props.modelId,
},
),
}}
icon={IconProp.Info}
/>
</SideMenuSection>
<SideMenuSection title="Advanced">
<SideMenuItem
link={{
title: "Delete",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.USER_DELETE] as Route,
{
modelId: props.modelId,
},
),
}}
icon={IconProp.Trash}
/>
</SideMenuSection>
</SideMenu>
);
};
export default SideMenuComponent;

View File

@ -3,8 +3,14 @@ enum PageMap {
HOME = "HOME",
LOGOUT = "LOGOUT",
SETTINGS = "SETTINGS",
USERS = "USERS",
USER_VIEW = "USER_VIEW",
USER_DELETE = "USER_DELETE",
PROJECTS = "PROJECTS",
PROJECT_VIEW = "PROJECT_VIEW",
PROJECT_DELETE = "PROJECT_DELETE",
SETTINGS_HOST = "SETTINGS_HOST",
SETTINGS_SMTP = "SETTINGS_SMTP",

View File

@ -9,8 +9,19 @@ const RouteMap: Dictionary<Route> = {
[PageMap.HOME]: new Route(`/admin`),
[PageMap.LOGOUT]: new Route(`/admin/logout`),
[PageMap.SETTINGS]: new Route(`/admin/settings/host`),
[PageMap.PROJECTS]: new Route(`/admin/projects`),
[PageMap.PROJECT_VIEW]: new Route(`/admin/projects/${RouteParams.ModelID}`),
[PageMap.PROJECT_DELETE]: new Route(
`/admin/projects/${RouteParams.ModelID}/delete`,
),
[PageMap.USERS]: new Route(`/admin/users`),
[PageMap.USER_VIEW]: new Route(`/admin/users/${RouteParams.ModelID}`),
[PageMap.USER_DELETE]: new Route(
`/admin/users/${RouteParams.ModelID}/delete`,
),
[PageMap.SETTINGS_HOST]: new Route(`/admin/settings/host`),
[PageMap.SETTINGS_SMTP]: new Route(`/admin/settings/smtp`),
[PageMap.SETTINGS_CALL_AND_SMS]: new Route(`/admin/settings/call-and-sms`),