mirror of
https://github.com/OneUptime/oneuptime
synced 2024-11-21 22:59:07 +00:00
fix fmt
This commit is contained in:
parent
b609742b7a
commit
d3216b0d5f
@ -1,5 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import React from 'react';
|
||||
import {
|
||||
Routes,
|
||||
Route as PageRoute,
|
||||
@ -11,6 +10,10 @@ import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import User from 'CommonUI/src/Utils/User';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import { ACCOUNTS_URL } from 'CommonUI/src/Config';
|
||||
import MasterPage from './Components/MasterPage/MasterPage';
|
||||
import RouteMap from './Utils/RouteMap';
|
||||
import PageMap from './Utils/PageMap';
|
||||
import Init from './Pages/Init/Init';
|
||||
|
||||
const App: () => JSX.Element = () => {
|
||||
Navigation.setNavigateHook(useNavigate());
|
||||
@ -30,11 +33,15 @@ const App: () => JSX.Element = () => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<></>
|
||||
<MasterPage>
|
||||
<Routes>
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.INIT]?.toString() || ''}
|
||||
element={<Init />}
|
||||
/>
|
||||
</Routes>
|
||||
</MasterPage>
|
||||
);
|
||||
};
|
||||
|
||||
|
129
AdminDashboard /src/Components/Footer/Footer.tsx
Normal file
129
AdminDashboard /src/Components/Footer/Footer.tsx
Normal file
@ -0,0 +1,129 @@
|
||||
import React from 'react';
|
||||
import Footer from 'CommonUI/src/Components/Footer/Footer';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import API from 'Common/Utils/API';
|
||||
import { DOMAIN, HTTP_PROTOCOL } from 'CommonUI/src/Config';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import ConfirmModal from 'CommonUI/src/Components/Modal/ConfirmModal';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import HTTPResponse from 'Common/Types/API/HTTPResponse';
|
||||
|
||||
const DashboardFooter: () => JSX.Element = () => {
|
||||
const [showAboutModal, setShowAboutModal] = React.useState<boolean>(false);
|
||||
const [isAboutModalLoading, setIsAboutModalLoading] =
|
||||
React.useState<boolean>(false);
|
||||
const [versionText, setVersionText] = React.useState<Dictionary<string>>(
|
||||
{}
|
||||
);
|
||||
|
||||
const fetchVersions: () => Promise<void> = async (): Promise<void> => {
|
||||
setIsAboutModalLoading(true);
|
||||
|
||||
try {
|
||||
const verText: Dictionary<string> = {};
|
||||
const apps: Array<{
|
||||
name: string;
|
||||
path: string;
|
||||
}> = [
|
||||
{
|
||||
name: 'API',
|
||||
path: '/api',
|
||||
},
|
||||
{
|
||||
name: 'Dashboard',
|
||||
path: '/dashboard',
|
||||
},
|
||||
{
|
||||
name: 'Notification',
|
||||
path: '/notification',
|
||||
},
|
||||
{
|
||||
name: 'Identity Service',
|
||||
path: '/identity',
|
||||
},
|
||||
];
|
||||
|
||||
for (const app of apps) {
|
||||
const version: JSONObject = await fetchAppVersion(app.path);
|
||||
verText[
|
||||
app.name
|
||||
] = `${app.name}: ${version['version']} (${version['commit']})`;
|
||||
}
|
||||
|
||||
setVersionText(verText);
|
||||
} catch (err) {
|
||||
setVersionText({
|
||||
error:
|
||||
'Version data is not available: ' + (err as Error).message,
|
||||
});
|
||||
}
|
||||
|
||||
setIsAboutModalLoading(false);
|
||||
};
|
||||
|
||||
const fetchAppVersion: (appName: string) => Promise<JSONObject> = async (
|
||||
appName: string
|
||||
): Promise<JSONObject> => {
|
||||
const response: HTTPResponse<JSONObject> = await API.get<JSONObject>(
|
||||
URL.fromString(`${HTTP_PROTOCOL}/${DOMAIN}${appName}/version`)
|
||||
);
|
||||
|
||||
if (response.data) {
|
||||
return response.data as JSONObject;
|
||||
}
|
||||
throw new BadDataException('Version data is not available');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Footer
|
||||
className="bg-white h-16 inset-x-0 bottom-0 px-8"
|
||||
copyright="HackerBay, Inc."
|
||||
links={[
|
||||
{
|
||||
title: 'Help and Support',
|
||||
to: URL.fromString('https://oneuptime.com/support'),
|
||||
},
|
||||
{
|
||||
title: 'Legal',
|
||||
to: URL.fromString('https://oneuptime.com/legal'),
|
||||
},
|
||||
{
|
||||
title: 'Version',
|
||||
onClick: async () => {
|
||||
setShowAboutModal(true);
|
||||
await fetchVersions();
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{showAboutModal ? (
|
||||
<ConfirmModal
|
||||
title={`OneUptime Version`}
|
||||
description={
|
||||
<div>
|
||||
{Object.keys(versionText).map(
|
||||
(key: string, i: number) => {
|
||||
return (
|
||||
<div key={i}>{versionText[key]}</div>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
isLoading={isAboutModalLoading}
|
||||
submitButtonText={'Close'}
|
||||
onSubmit={() => {
|
||||
return setShowAboutModal(false);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardFooter;
|
34
AdminDashboard /src/Components/Header/Header.tsx
Normal file
34
AdminDashboard /src/Components/Header/Header.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import Help from './Help';
|
||||
import Header from 'CommonUI/src/Components/Header/Header';
|
||||
import Logo from './Logo';
|
||||
|
||||
const DashboardHeader: FunctionComponent = (): ReactElement => {
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
leftComponents={
|
||||
<>
|
||||
<Logo onClick={() => {}} />
|
||||
</>
|
||||
}
|
||||
centerComponents={
|
||||
<>
|
||||
{/* <SearchBox
|
||||
key={2}
|
||||
selectedProject={props.selectedProject}
|
||||
onChange={(_value: string) => { }}
|
||||
/>{' '} */}
|
||||
</>
|
||||
}
|
||||
rightComponents={
|
||||
<>
|
||||
<Help />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardHeader;
|
60
AdminDashboard /src/Components/Header/Help.tsx
Normal file
60
AdminDashboard /src/Components/Header/Help.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
import React, { ReactElement, useState } from 'react';
|
||||
import HeaderIconDropdownButton from 'CommonUI/src/Components/Header/HeaderIconDropdownButton';
|
||||
import IconDropdownItem from 'CommonUI/src/Components/Header/IconDropdown/IconDropdownItem';
|
||||
import IconDropdownMenu from 'CommonUI/src/Components/Header/IconDropdown/IconDropdownMenu';
|
||||
import IconDropdownRow from 'CommonUI/src/Components/Header/IconDropdown/IconDropdownRow';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
|
||||
const Help: () => JSX.Element = (): ReactElement => {
|
||||
const [isDropdownVisible, setIsDropdownVisible] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<HeaderIconDropdownButton
|
||||
icon={IconProp.Help}
|
||||
name="Help"
|
||||
showDropdown={isDropdownVisible}
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(true);
|
||||
}}
|
||||
>
|
||||
<IconDropdownMenu>
|
||||
<IconDropdownRow>
|
||||
<IconDropdownItem
|
||||
title="Support Email"
|
||||
icon={IconProp.Email}
|
||||
openInNewTab={true}
|
||||
url={URL.fromString('mailto:support@oneuptime.com')}
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(false);
|
||||
}}
|
||||
/>
|
||||
<IconDropdownItem
|
||||
title="Chat on Slack"
|
||||
icon={IconProp.Slack}
|
||||
openInNewTab={true}
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(false);
|
||||
}}
|
||||
url={URL.fromString(
|
||||
'https://join.slack.com/t/oneuptimesupport/shared_invite/zt-1kavkds2f-gegm_wePorvwvM3M_SaoCQ'
|
||||
)}
|
||||
/>
|
||||
<IconDropdownItem
|
||||
title="Request Demo"
|
||||
icon={IconProp.Window}
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(false);
|
||||
}}
|
||||
openInNewTab={true}
|
||||
url={URL.fromString(
|
||||
'https://oneuptime.com/enterprise/demo'
|
||||
)}
|
||||
/>
|
||||
</IconDropdownRow>
|
||||
</IconDropdownMenu>
|
||||
</HeaderIconDropdownButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default Help;
|
31
AdminDashboard /src/Components/Header/Logo.tsx
Normal file
31
AdminDashboard /src/Components/Header/Logo.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
// Tailwind
|
||||
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import Image from 'CommonUI/src/Components/Image/Image';
|
||||
import OneUptimeLogo from 'CommonUI/src/Images/logos/OneUptimeSVG/3-transparent.svg';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
|
||||
export interface ComponentProps {
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
const Logo: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps
|
||||
): ReactElement => {
|
||||
return (
|
||||
<div className="relative z-10 flex px-2 lg:px-0">
|
||||
<div className="flex flex-shrink-0 items-center">
|
||||
<Image
|
||||
className="block h-8 w-auto"
|
||||
onClick={() => {
|
||||
props.onClick && props.onClick();
|
||||
}}
|
||||
imageUrl={Route.fromString(`${OneUptimeLogo}`)}
|
||||
alt={'OneUptime'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Logo;
|
35
AdminDashboard /src/Components/Header/Notifications.tsx
Normal file
35
AdminDashboard /src/Components/Header/Notifications.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import React, { ReactElement, useState } from 'react';
|
||||
import HeaderIconDropdownButton from 'CommonUI/src/Components/Header/HeaderIconDropdownButton';
|
||||
import Notifications from 'CommonUI/src/Components/Header/Notifications/Notifications';
|
||||
import NotificationItem from 'CommonUI/src/Components/Header/Notifications/NotificationItem';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
|
||||
const DashboardHeader: () => JSX.Element = (): ReactElement => {
|
||||
const [isDropdownVisible, setIsDropdownVisible] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<HeaderIconDropdownButton
|
||||
name="Notifications"
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(true);
|
||||
}}
|
||||
showDropdown={isDropdownVisible}
|
||||
icon={IconProp.Notification}
|
||||
badge={4}
|
||||
>
|
||||
<Notifications>
|
||||
<NotificationItem
|
||||
title="Sample Title"
|
||||
description="Sample Description"
|
||||
createdAt={new Date()}
|
||||
icon={IconProp.Home}
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(false);
|
||||
}}
|
||||
/>
|
||||
</Notifications>
|
||||
</HeaderIconDropdownButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardHeader;
|
285
AdminDashboard /src/Components/Header/ProjectPicker.tsx
Normal file
285
AdminDashboard /src/Components/Header/ProjectPicker.tsx
Normal file
@ -0,0 +1,285 @@
|
||||
import React, {
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
useState,
|
||||
useEffect,
|
||||
} from 'react';
|
||||
import ProjectPicker from 'CommonUI/src/Components/Header/ProjectPicker/ProjectPicker';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import Project from 'Model/Models/Project';
|
||||
import ModelFormModal from 'CommonUI/src/Components/ModelFormModal/ModelFormModal';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import { FormType } from 'CommonUI/src/Components/Forms/ModelForm';
|
||||
import ProjectUtil from 'CommonUI/src/Utils/Project';
|
||||
import { BILLING_ENABLED, getAllEnvVars } from 'CommonUI/src/Config';
|
||||
import SubscriptionPlan from 'Common/Types/Billing/SubscriptionPlan';
|
||||
import Field from 'CommonUI/src/Components/Forms/Types/Field';
|
||||
import { RadioButton } from 'CommonUI/src/Components/RadioButtons/RadioButtons';
|
||||
import Toggle from 'CommonUI/src/Components/Toggle/Toggle';
|
||||
|
||||
export interface ComponentProps {
|
||||
projects: Array<Project>;
|
||||
onProjectSelected: (project: Project) => void;
|
||||
showProjectModal: boolean;
|
||||
onProjectModalClose: () => void;
|
||||
}
|
||||
|
||||
const DashboardProjectPicker: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps
|
||||
): ReactElement => {
|
||||
const [showModal, setShowModal] = useState<boolean>(false);
|
||||
const [selectedProject, setSelectedProject] = useState<Project | null>(
|
||||
null
|
||||
);
|
||||
|
||||
const getFooter: Function = (): ReactElement => {
|
||||
if (!BILLING_ENABLED) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Toggle
|
||||
title="Yearly Plan"
|
||||
initialValue={isSubscriptionPlanYearly}
|
||||
description="(Save 20%)"
|
||||
onChange={(value: boolean) => {
|
||||
setIsSubscriptionPlanYearly(value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const [isSubscriptionPlanYearly, setIsSubscriptionPlanYearly] =
|
||||
useState<boolean>(true);
|
||||
|
||||
const [fields, setFields] = useState<Array<Field<Project>>>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.showProjectModal) {
|
||||
setShowModal(true);
|
||||
}
|
||||
}, [props.showProjectModal]);
|
||||
|
||||
useEffect(() => {
|
||||
const currentProject: Project | null = ProjectUtil.getCurrentProject();
|
||||
setSelectedProject(currentProject);
|
||||
if (currentProject && props.onProjectSelected) {
|
||||
props.onProjectSelected(currentProject);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedProject) {
|
||||
ProjectUtil.setCurrentProject(selectedProject);
|
||||
if (props.onProjectSelected) {
|
||||
props.onProjectSelected(selectedProject);
|
||||
}
|
||||
}
|
||||
}, [selectedProject]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
props.projects &&
|
||||
props.projects.length > 0 &&
|
||||
!selectedProject &&
|
||||
props.projects[0]
|
||||
) {
|
||||
const currentProject: Project | null =
|
||||
ProjectUtil.getCurrentProject();
|
||||
|
||||
if (!currentProject) {
|
||||
setSelectedProject(props.projects[0]);
|
||||
} else if (
|
||||
props.projects.filter((project: Project) => {
|
||||
return project._id === currentProject._id;
|
||||
}).length > 0
|
||||
) {
|
||||
setSelectedProject(
|
||||
props.projects.filter((project: Project) => {
|
||||
return project._id === currentProject._id;
|
||||
})[0] as Project
|
||||
);
|
||||
} else {
|
||||
setSelectedProject(props.projects[0]);
|
||||
}
|
||||
}
|
||||
}, [props.projects]);
|
||||
|
||||
useEffect(() => {
|
||||
refreshFields();
|
||||
}, [isSubscriptionPlanYearly]);
|
||||
|
||||
const refreshFields: Function = (): void => {
|
||||
let formFields: Array<Field<Project>> = [
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
validation: {
|
||||
minLength: 4,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
placeholder: 'My Project',
|
||||
description: 'Pick a friendly name.',
|
||||
title: 'Project Name',
|
||||
required: true,
|
||||
stepId: BILLING_ENABLED ? 'basic' : undefined,
|
||||
},
|
||||
];
|
||||
|
||||
if (BILLING_ENABLED) {
|
||||
formFields = [
|
||||
...formFields,
|
||||
{
|
||||
field: {
|
||||
paymentProviderPlanId: true,
|
||||
},
|
||||
stepId: 'plan',
|
||||
validation: {
|
||||
minLength: 6,
|
||||
},
|
||||
footerElement: getFooter(),
|
||||
fieldType: FormFieldSchemaType.RadioButton,
|
||||
radioButtonOptions: SubscriptionPlan.getSubscriptionPlans(
|
||||
getAllEnvVars()
|
||||
).map((plan: SubscriptionPlan): RadioButton => {
|
||||
let description: string = plan.isCustomPricing()
|
||||
? `Our sales team will contact you soon.`
|
||||
: `Billed ${
|
||||
isSubscriptionPlanYearly
|
||||
? 'yearly'
|
||||
: 'monthly'
|
||||
}. ${
|
||||
plan.getTrialPeriod() > 0
|
||||
? `Free ${plan.getTrialPeriod()} days trial.`
|
||||
: ''
|
||||
}`;
|
||||
|
||||
if (
|
||||
isSubscriptionPlanYearly &&
|
||||
plan.getYearlySubscriptionAmountInUSD() === 0
|
||||
) {
|
||||
description = 'This plan is free, forever. ';
|
||||
}
|
||||
|
||||
if (
|
||||
!isSubscriptionPlanYearly &&
|
||||
plan.getMonthlySubscriptionAmountInUSD() === 0
|
||||
) {
|
||||
description = 'This plan is free, forever. ';
|
||||
}
|
||||
|
||||
return {
|
||||
value: isSubscriptionPlanYearly
|
||||
? plan.getYearlyPlanId()
|
||||
: plan.getMonthlyPlanId(),
|
||||
title: plan.getName(),
|
||||
description: description,
|
||||
sideTitle: plan.isCustomPricing()
|
||||
? 'Custom Price'
|
||||
: isSubscriptionPlanYearly
|
||||
? '$' +
|
||||
plan
|
||||
.getYearlySubscriptionAmountInUSD()
|
||||
.toString() +
|
||||
'/mo billed yearly'
|
||||
: '$' +
|
||||
plan
|
||||
.getMonthlySubscriptionAmountInUSD()
|
||||
.toString(),
|
||||
sideDescription: plan.isCustomPricing()
|
||||
? ''
|
||||
: isSubscriptionPlanYearly
|
||||
? `~ $${
|
||||
plan.getYearlySubscriptionAmountInUSD() *
|
||||
12
|
||||
} per user / year`
|
||||
: `/month per user`,
|
||||
};
|
||||
}),
|
||||
title: 'Please select a plan.',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
paymentProviderPromoCode: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
placeholder: 'Promo Code (Optional)',
|
||||
description: 'If you have a coupon code, enter it here.',
|
||||
title: 'Promo Code',
|
||||
required: false,
|
||||
stepId: 'plan',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
setFields(formFields);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.projects.length !== 0 && (
|
||||
<ProjectPicker
|
||||
selectedProjectName={selectedProject?.name || ''}
|
||||
selectedProjectIcon={IconProp.Folder}
|
||||
projects={props.projects}
|
||||
onCreateProjectButtonClicked={() => {
|
||||
setShowModal(true);
|
||||
props.onProjectModalClose();
|
||||
}}
|
||||
onProjectSelected={(project: Project) => {
|
||||
setSelectedProject(project);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{showModal ? (
|
||||
<ModelFormModal<Project>
|
||||
modelType={Project}
|
||||
name="Create New Project"
|
||||
title="Create New Project"
|
||||
description="Please create a new OneUptime project to get started."
|
||||
onClose={() => {
|
||||
setShowModal(false);
|
||||
props.onProjectModalClose();
|
||||
}}
|
||||
submitButtonText="Create Project"
|
||||
onSuccess={(project: Project | null) => {
|
||||
setSelectedProject(project);
|
||||
if (project && props.onProjectSelected) {
|
||||
props.onProjectSelected(project);
|
||||
}
|
||||
setShowModal(false);
|
||||
props.onProjectModalClose();
|
||||
}}
|
||||
formProps={{
|
||||
name: 'Create New Project',
|
||||
steps: BILLING_ENABLED
|
||||
? [
|
||||
{
|
||||
title: 'Basic',
|
||||
id: 'basic',
|
||||
},
|
||||
{
|
||||
title: 'Select Plan',
|
||||
id: 'plan',
|
||||
},
|
||||
]
|
||||
: undefined,
|
||||
saveRequestOptions: {
|
||||
isMultiTenantRequest: true, // because this is a tenant request, we do not have to include the header in the request
|
||||
},
|
||||
modelType: Project,
|
||||
id: 'create-project-from',
|
||||
fields: [...fields],
|
||||
formType: FormType.Create,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardProjectPicker;
|
20
AdminDashboard /src/Components/Header/SearchBox.tsx
Normal file
20
AdminDashboard /src/Components/Header/SearchBox.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import SearchBox from 'CommonUI/src/Components/Header/SearchBox';
|
||||
import Project from 'Model/Models/Project';
|
||||
|
||||
export interface ComponentProps {
|
||||
onChange: (search: string) => void;
|
||||
selectedProject: Project | null;
|
||||
}
|
||||
|
||||
const Search: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps
|
||||
): ReactElement => {
|
||||
if (!props.selectedProject) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return <SearchBox key={2} onChange={props.onChange} />;
|
||||
};
|
||||
|
||||
export default Search;
|
161
AdminDashboard /src/Components/Header/Upgrade.tsx
Normal file
161
AdminDashboard /src/Components/Header/Upgrade.tsx
Normal file
@ -0,0 +1,161 @@
|
||||
import React, { ReactElement, useState } from 'react';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import ModelFormModal from 'CommonUI/src/Components/ModelFormModal/ModelFormModal';
|
||||
import Project from 'Model/Models/Project';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import { FormType } from 'CommonUI/src/Components/Forms/ModelForm';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import SubscriptionPlan from 'Common/Types/Billing/SubscriptionPlan';
|
||||
import { RadioButton } from 'CommonUI/src/Components/RadioButtons/RadioButtons';
|
||||
import Button, { ButtonStyleType } from 'CommonUI/src/Components/Button/Button';
|
||||
import { BILLING_ENABLED, getAllEnvVars } from 'CommonUI/src/Config';
|
||||
import DashboardNavigation from '../../Utils/Navigation';
|
||||
import Toggle from 'CommonUI/src/Components/Toggle/Toggle';
|
||||
|
||||
const Upgrade: () => JSX.Element = (): ReactElement => {
|
||||
const [showModal, setShowModal] = useState<boolean>(false);
|
||||
const [isSubscriptionPlanYearly, setIsSubscriptionPlanYearly] =
|
||||
useState<boolean>(true);
|
||||
|
||||
const getFooter: Function = (): ReactElement => {
|
||||
if (!BILLING_ENABLED) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Toggle
|
||||
title="Yearly Plan"
|
||||
initialValue={isSubscriptionPlanYearly}
|
||||
description="(Save 20%)"
|
||||
onChange={(value: boolean) => {
|
||||
setIsSubscriptionPlanYearly(value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
title="Upgrade Plan"
|
||||
onClick={() => {
|
||||
setShowModal(true);
|
||||
}}
|
||||
buttonStyle={ButtonStyleType.LINK}
|
||||
icon={IconProp.Star}
|
||||
></Button>
|
||||
{showModal ? (
|
||||
<ModelFormModal<Project>
|
||||
modelType={Project}
|
||||
title="Change Plan"
|
||||
name="Change Plan"
|
||||
modelIdToEdit={DashboardNavigation.getProjectId()!}
|
||||
onClose={() => {
|
||||
setShowModal(false);
|
||||
}}
|
||||
submitButtonText="Change Plan"
|
||||
onSuccess={() => {
|
||||
Navigation.reload();
|
||||
}}
|
||||
formProps={{
|
||||
name: 'Change Plan',
|
||||
saveRequestOptions: {
|
||||
isMultiTenantRequest: true, // because this is a tenant request, we do not have to include the header in the request
|
||||
},
|
||||
modelType: Project,
|
||||
id: 'create-project-from',
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
paymentProviderPlanId: true,
|
||||
},
|
||||
validation: {
|
||||
minLength: 6,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.RadioButton,
|
||||
radioButtonOptions:
|
||||
SubscriptionPlan.getSubscriptionPlans(
|
||||
getAllEnvVars()
|
||||
).map(
|
||||
(
|
||||
plan: SubscriptionPlan
|
||||
): RadioButton => {
|
||||
let description: string =
|
||||
plan.isCustomPricing()
|
||||
? `Our sales team will contact you soon.`
|
||||
: `Billed ${
|
||||
isSubscriptionPlanYearly
|
||||
? 'yearly'
|
||||
: 'monthly'
|
||||
}. ${
|
||||
plan.getTrialPeriod() >
|
||||
0
|
||||
? `Free ${plan.getTrialPeriod()} days trial.`
|
||||
: ''
|
||||
}`;
|
||||
|
||||
if (
|
||||
isSubscriptionPlanYearly &&
|
||||
plan.getYearlySubscriptionAmountInUSD() ===
|
||||
0
|
||||
) {
|
||||
description =
|
||||
'This plan is free, forever. ';
|
||||
}
|
||||
|
||||
if (
|
||||
!isSubscriptionPlanYearly &&
|
||||
plan.getMonthlySubscriptionAmountInUSD() ===
|
||||
0
|
||||
) {
|
||||
description =
|
||||
'This plan is free, forever. ';
|
||||
}
|
||||
|
||||
return {
|
||||
value: isSubscriptionPlanYearly
|
||||
? plan.getYearlyPlanId()
|
||||
: plan.getMonthlyPlanId(),
|
||||
title: plan.getName(),
|
||||
description: description,
|
||||
sideTitle:
|
||||
plan.isCustomPricing()
|
||||
? 'Custom Price'
|
||||
: isSubscriptionPlanYearly
|
||||
? '$' +
|
||||
plan
|
||||
.getYearlySubscriptionAmountInUSD()
|
||||
.toString() +
|
||||
'/mo billed yearly'
|
||||
: '$' +
|
||||
plan
|
||||
.getMonthlySubscriptionAmountInUSD()
|
||||
.toString(),
|
||||
sideDescription:
|
||||
plan.isCustomPricing()
|
||||
? ''
|
||||
: isSubscriptionPlanYearly
|
||||
? `~ $${
|
||||
plan.getYearlySubscriptionAmountInUSD() *
|
||||
12
|
||||
} per user / year`
|
||||
: `/month per user`,
|
||||
};
|
||||
}
|
||||
),
|
||||
title: 'Please select a plan.',
|
||||
required: true,
|
||||
footerElement: getFooter(),
|
||||
},
|
||||
],
|
||||
formType: FormType.Update,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Upgrade;
|
56
AdminDashboard /src/Components/Header/UserProfile.tsx
Normal file
56
AdminDashboard /src/Components/Header/UserProfile.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import React, { FunctionComponent, ReactElement, useState } from 'react';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import RouteMap, { RouteUtil } from '../../Utils/RouteMap';
|
||||
import PageMap from '../../Utils/PageMap';
|
||||
import BlankProfilePic from 'CommonUI/src/Images/users/blank-profile.svg';
|
||||
import HeaderIconDropdownButton from 'CommonUI/src/Components/Header/HeaderIconDropdownButton';
|
||||
import IconDropdownItem from 'CommonUI/src/Components/Header/IconDropdown/IconDropdownItem';
|
||||
import IconDropdownMenu from 'CommonUI/src/Components/Header/IconDropdown/IconDropdownMenu';
|
||||
|
||||
export interface ComponentProps {
|
||||
onClickUserProfile: () => void;
|
||||
}
|
||||
|
||||
const DashboardUserProfile: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps
|
||||
): ReactElement => {
|
||||
const [isDropdownVisible, setIsDropdownVisible] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderIconDropdownButton
|
||||
iconImageUrl={BlankProfilePic}
|
||||
name="User Profile"
|
||||
showDropdown={isDropdownVisible}
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(true);
|
||||
}}
|
||||
>
|
||||
<IconDropdownMenu>
|
||||
<IconDropdownItem
|
||||
title="Profile"
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(false);
|
||||
props.onClickUserProfile();
|
||||
}}
|
||||
icon={IconProp.User}
|
||||
/>
|
||||
|
||||
<IconDropdownItem
|
||||
title="Log out"
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(false);
|
||||
}}
|
||||
url={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.LOGOUT] as Route
|
||||
)}
|
||||
icon={IconProp.Logout}
|
||||
/>
|
||||
</IconDropdownMenu>
|
||||
</HeaderIconDropdownButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardUserProfile;
|
@ -0,0 +1,29 @@
|
||||
import MasterPage from 'CommonUI/src/Components/MasterPage/MasterPage';
|
||||
import Footer from '../Footer/Footer';
|
||||
import Header from '../Header/Header';
|
||||
import NavBar from '../NavBar/NavBar';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
|
||||
export interface ComponentProps {
|
||||
children: ReactElement | Array<ReactElement>;
|
||||
}
|
||||
|
||||
const DashboardMasterPage: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps
|
||||
): ReactElement => {
|
||||
|
||||
return (
|
||||
<MasterPage
|
||||
footer={<Footer />}
|
||||
header={<Header />}
|
||||
navBar={<NavBar />}
|
||||
isLoading={false}
|
||||
error={''}
|
||||
className="flex flex-col h-screen justify-between"
|
||||
>
|
||||
{props.children}
|
||||
</MasterPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardMasterPage;
|
201
AdminDashboard /src/Components/NavBar/NavBar.tsx
Normal file
201
AdminDashboard /src/Components/NavBar/NavBar.tsx
Normal file
@ -0,0 +1,201 @@
|
||||
import React, { FunctionComponent, ReactElement, useState } from 'react';
|
||||
import NavBar from 'CommonUI/src/Components/Navbar/NavBar';
|
||||
import NavBarMenu from 'CommonUI/src/Components/Navbar/NavBarMenu';
|
||||
import NavBarItem from 'CommonUI/src/Components/Navbar/NavBarItem';
|
||||
import NavBarMenuItem from 'CommonUI/src/Components/Navbar/NavBarMenuItem';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import PageMap from '../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../Utils/RouteMap';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
|
||||
const DashboardNavbar: FunctionComponent = (): ReactElement => {
|
||||
const [isComponentVisible, setIsComponentVisible] =
|
||||
useState<boolean>(false);
|
||||
const [moreMenuTimeout, setMoreMenuTimeout] = useState<ReturnType<
|
||||
typeof setTimeout
|
||||
> | null>(null);
|
||||
|
||||
const hideMoreMenu: Function = (): void => {
|
||||
if (moreMenuTimeout) {
|
||||
clearTimeout(moreMenuTimeout);
|
||||
setMoreMenuTimeout(null);
|
||||
}
|
||||
|
||||
const timeout: ReturnType<typeof setTimeout> = setTimeout(() => {
|
||||
setIsComponentVisible(false);
|
||||
}, 500);
|
||||
|
||||
setMoreMenuTimeout(timeout);
|
||||
};
|
||||
|
||||
const forceHideMoreMenu: Function = (): void => {
|
||||
if (moreMenuTimeout) {
|
||||
clearTimeout(moreMenuTimeout);
|
||||
setMoreMenuTimeout(null);
|
||||
}
|
||||
|
||||
setIsComponentVisible(false);
|
||||
};
|
||||
|
||||
const showMoreMenu: Function = (): void => {
|
||||
if (moreMenuTimeout) {
|
||||
clearTimeout(moreMenuTimeout);
|
||||
}
|
||||
setIsComponentVisible(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<NavBar>
|
||||
<NavBarItem
|
||||
title="Home"
|
||||
icon={IconProp.Home}
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route
|
||||
)}
|
||||
></NavBarItem>
|
||||
|
||||
<NavBarItem
|
||||
title="Monitors"
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITORS] as Route
|
||||
)}
|
||||
icon={IconProp.AltGlobe}
|
||||
></NavBarItem>
|
||||
|
||||
<NavBarItem
|
||||
title="Incidents"
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENTS] as Route
|
||||
)}
|
||||
icon={IconProp.Alert}
|
||||
></NavBarItem>
|
||||
|
||||
<NavBarItem
|
||||
title="Scheduled Maintenance"
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SCHEDULED_MAINTENANCE_EVENTS] as Route
|
||||
)}
|
||||
icon={IconProp.Clock}
|
||||
></NavBarItem>
|
||||
|
||||
<NavBarItem
|
||||
title="Status Pages"
|
||||
icon={IconProp.CheckCircle}
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.STATUS_PAGES] as Route
|
||||
)}
|
||||
></NavBarItem>
|
||||
|
||||
<NavBarItem
|
||||
title="More"
|
||||
icon={IconProp.More}
|
||||
onMouseLeave={() => {
|
||||
hideMoreMenu();
|
||||
}}
|
||||
onMouseOver={() => {
|
||||
showMoreMenu();
|
||||
}}
|
||||
onClick={() => {
|
||||
showMoreMenu();
|
||||
}}
|
||||
>
|
||||
<div
|
||||
onMouseOver={() => {
|
||||
showMoreMenu();
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
hideMoreMenu();
|
||||
}}
|
||||
>
|
||||
{isComponentVisible && (
|
||||
<NavBarMenu
|
||||
footer={{
|
||||
title: 'Report a bug or request a feature.',
|
||||
description:
|
||||
'We embrace open source! Please report any issue you find and make feature requests on GitHub.',
|
||||
link: URL.fromString(
|
||||
'https://github.com/OneUptime/oneuptime/issues/new/choose'
|
||||
),
|
||||
}}
|
||||
>
|
||||
<NavBarMenuItem
|
||||
title="On-Call Duty"
|
||||
description="Manage your on-call schedules, escalations and more."
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.ON_CALL_DUTY] as Route
|
||||
)}
|
||||
icon={IconProp.Call}
|
||||
onClick={() => {
|
||||
forceHideMoreMenu();
|
||||
}}
|
||||
/>
|
||||
|
||||
<NavBarMenuItem
|
||||
title="Workflows"
|
||||
description="Integrate OneUptime with the rest of your ecosystem."
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.WORKFLOWS] as Route
|
||||
)}
|
||||
icon={IconProp.Workflow}
|
||||
onClick={() => {
|
||||
forceHideMoreMenu();
|
||||
}}
|
||||
/>
|
||||
<NavBarMenuItem
|
||||
title="Project Settings"
|
||||
description="Review or manage settings related to this project here."
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS] as Route
|
||||
)}
|
||||
icon={IconProp.Settings}
|
||||
onClick={() => {
|
||||
forceHideMoreMenu();
|
||||
}}
|
||||
/>
|
||||
<NavBarMenuItem
|
||||
title="User Settings"
|
||||
description="Review or manage user settings related to this project here."
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.USER_SETTINGS] as Route
|
||||
)}
|
||||
icon={IconProp.User}
|
||||
onClick={() => {
|
||||
forceHideMoreMenu();
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* <NavBarMenuItem
|
||||
title="Logs Management"
|
||||
description='Manage your application logs.'
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.LOGS] as Route
|
||||
)}
|
||||
icon={IconProp.Terminal}
|
||||
/>
|
||||
<NavBarMenuItem
|
||||
title="Error Tracker"
|
||||
description='Manage your application errors.'
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.ERROR_TRACKER] as Route
|
||||
)}
|
||||
icon={IconProp.Error}
|
||||
/>
|
||||
|
||||
<NavBarMenuItem
|
||||
title="Reports"
|
||||
description='Get insights into your Observability process.'
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.REPORTS] as Route
|
||||
)}
|
||||
icon={IconProp.Report}
|
||||
/> */}
|
||||
</NavBarMenu>
|
||||
)}
|
||||
</div>
|
||||
</NavBarItem>
|
||||
</NavBar>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardNavbar;
|
13
AdminDashboard /src/Pages/Init/Init.tsx
Normal file
13
AdminDashboard /src/Pages/Init/Init.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import PageLoader from 'CommonUI/src/Components/Loader/PageLoader';
|
||||
import Page from 'CommonUI/src/Components/Page/Page';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
|
||||
const Init: FunctionComponent = (): ReactElement => {
|
||||
return (
|
||||
<Page title={''} breadcrumbLinks={[]}>
|
||||
<PageLoader isVisible={true} />
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default Init;
|
5
AdminDashboard /src/Utils/PageMap.ts
Normal file
5
AdminDashboard /src/Utils/PageMap.ts
Normal file
@ -0,0 +1,5 @@
|
||||
enum PageMap {
|
||||
INIT = 'INIT',
|
||||
}
|
||||
|
||||
export default PageMap;
|
41
AdminDashboard /src/Utils/RouteMap.ts
Normal file
41
AdminDashboard /src/Utils/RouteMap.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import PageMap from './PageMap';
|
||||
import RouteParams from './RouteParams';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
|
||||
const RouteMap: Dictionary<Route> = {
|
||||
[PageMap.INIT]: new Route(`/admin-dashboard`),
|
||||
};
|
||||
|
||||
export class RouteUtil {
|
||||
public static populateRouteParams(
|
||||
route: Route,
|
||||
props?: {
|
||||
modelId?: ObjectID;
|
||||
subModelId?: ObjectID;
|
||||
}
|
||||
): Route {
|
||||
// populate projectid
|
||||
|
||||
const tempRoute: Route = new Route(route.toString());
|
||||
|
||||
if (props && props.modelId) {
|
||||
route = tempRoute.addRouteParam(
|
||||
RouteParams.ModelID,
|
||||
props.modelId.toString()
|
||||
);
|
||||
}
|
||||
|
||||
if (props && props.subModelId) {
|
||||
route = tempRoute.addRouteParam(
|
||||
RouteParams.SubModelID,
|
||||
props.subModelId.toString()
|
||||
);
|
||||
}
|
||||
|
||||
return tempRoute;
|
||||
}
|
||||
}
|
||||
|
||||
export default RouteMap;
|
6
AdminDashboard /src/Utils/RouteParams.ts
Normal file
6
AdminDashboard /src/Utils/RouteParams.ts
Normal file
@ -0,0 +1,6 @@
|
||||
enum RouteParams {
|
||||
ModelID = ':id',
|
||||
SubModelID = ':subModelId',
|
||||
}
|
||||
|
||||
export default RouteParams;
|
Loading…
Reference in New Issue
Block a user