mirror of
https://github.com/sartography/spiff-arena.git
synced 2025-02-22 06:08:29 +00:00
some support for extensions in v3 ui w/ burnettk
This commit is contained in:
parent
12bf88de88
commit
298e50bbba
@ -6,8 +6,8 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { AbilityContext } from './contexts/Can';
|
||||
import APIErrorProvider from './contexts/APIErrorContext';
|
||||
import ContainerForExtensions from './ContainerForExtensions';
|
||||
import ContainerForExtensionsV3 from './a-spiffui-v3/ContainerForExtensions';
|
||||
import PublicRoutes from './a-spiffui-v3/views/PublicRoutes';
|
||||
import SpiffUIV3 from './a-spiffui-v3/views/SpiffUIV3';
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
@ -18,7 +18,7 @@ export default function App() {
|
||||
{ path: 'public/*', element: <PublicRoutes /> },
|
||||
{
|
||||
path: 'newui/*',
|
||||
element: <SpiffUIV3 />,
|
||||
element: <ContainerForExtensionsV3 />,
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
|
@ -0,0 +1,150 @@
|
||||
import { Container } from '@mui/material';
|
||||
import { Routes, Route, useLocation } from 'react-router-dom';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
|
||||
import ScrollToTop from './components/ScrollToTop';
|
||||
import Extension from './views/Extension';
|
||||
import { useUriListForPermissions } from './hooks/UriListForPermissions';
|
||||
import { PermissionsToCheck, ProcessFile, ProcessModel } from './interfaces';
|
||||
import { usePermissionFetcher } from './hooks/PermissionService';
|
||||
import {
|
||||
ExtensionUiSchema,
|
||||
UiSchemaUxElement,
|
||||
} from './extension_ui_schema_interfaces';
|
||||
import HttpService from './services/HttpService';
|
||||
import { ErrorBoundaryFallback } from './ErrorBoundaryFallack';
|
||||
import BaseRoutes from './views/BaseRoutes';
|
||||
import BackendIsDown from './views/BackendIsDown';
|
||||
import Login from './views/Login';
|
||||
import useAPIError from './hooks/UseApiError';
|
||||
|
||||
export default function ContainerForExtensions() {
|
||||
const [backendIsUp, setBackendIsUp] = useState<boolean | null>(null);
|
||||
const [extensionUxElements, setExtensionUxElements] = useState<
|
||||
UiSchemaUxElement[] | null
|
||||
>(null);
|
||||
|
||||
let contentClassName = 'main-site-body-centered';
|
||||
if (window.location.pathname.startsWith('/editor/')) {
|
||||
contentClassName = 'no-center-stuff';
|
||||
}
|
||||
const { targetUris } = useUriListForPermissions();
|
||||
const permissionRequestData: PermissionsToCheck = {
|
||||
[targetUris.extensionListPath]: ['GET'],
|
||||
};
|
||||
const { ability, permissionsLoaded } = usePermissionFetcher(
|
||||
permissionRequestData,
|
||||
);
|
||||
|
||||
const { removeError } = useAPIError();
|
||||
|
||||
const location = useLocation();
|
||||
|
||||
// never carry an error message across to a different path
|
||||
useEffect(() => {
|
||||
removeError();
|
||||
// if we include the removeError function to the dependency array of this useEffect, it causes
|
||||
// an infinite loop where the page with the error adds the error,
|
||||
// then this runs and it removes the error, etc. it is ok not to include it here, i think, because it never changes.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [location.pathname]);
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
useEffect(() => {
|
||||
const processExtensionResult = (processModels: ProcessModel[]) => {
|
||||
const eni: UiSchemaUxElement[] = processModels
|
||||
.map((processModel: ProcessModel) => {
|
||||
const extensionUiSchemaFile = processModel.files.find(
|
||||
(file: ProcessFile) => file.name === 'extension_uischema.json',
|
||||
);
|
||||
if (extensionUiSchemaFile && extensionUiSchemaFile.file_contents) {
|
||||
try {
|
||||
const extensionUiSchema: ExtensionUiSchema = JSON.parse(
|
||||
extensionUiSchemaFile.file_contents,
|
||||
);
|
||||
if (
|
||||
extensionUiSchema &&
|
||||
extensionUiSchema.ux_elements &&
|
||||
!extensionUiSchema.disabled
|
||||
) {
|
||||
return extensionUiSchema.ux_elements;
|
||||
}
|
||||
} catch (jsonParseError: any) {
|
||||
console.error(
|
||||
`Unable to get navigation items for ${processModel.id}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
return [] as UiSchemaUxElement[];
|
||||
})
|
||||
.flat();
|
||||
if (eni) {
|
||||
setExtensionUxElements(eni);
|
||||
}
|
||||
};
|
||||
|
||||
const getExtensions = () => {
|
||||
setBackendIsUp(true);
|
||||
if (!permissionsLoaded) {
|
||||
return;
|
||||
}
|
||||
if (ability.can('GET', targetUris.extensionListPath)) {
|
||||
HttpService.makeCallToBackend({
|
||||
path: targetUris.extensionListPath,
|
||||
successCallback: processExtensionResult,
|
||||
});
|
||||
} else {
|
||||
// set to an empty array so we know that it loaded
|
||||
setExtensionUxElements([]);
|
||||
}
|
||||
};
|
||||
|
||||
HttpService.makeCallToBackend({
|
||||
path: targetUris.statusPath,
|
||||
successCallback: getExtensions,
|
||||
failureCallback: () => setBackendIsUp(false),
|
||||
});
|
||||
}, [
|
||||
targetUris.extensionListPath,
|
||||
targetUris.statusPath,
|
||||
permissionsLoaded,
|
||||
ability,
|
||||
]);
|
||||
|
||||
const routeComponents = () => {
|
||||
return (
|
||||
<Routes>
|
||||
<Route
|
||||
path="*"
|
||||
element={<BaseRoutes extensionUxElements={extensionUxElements} />}
|
||||
/>
|
||||
<Route path="extensions/:page_identifier" element={<Extension />} />
|
||||
<Route path="login" element={<Login />} />
|
||||
</Routes>
|
||||
);
|
||||
};
|
||||
|
||||
const backendIsDownPage = () => {
|
||||
return [<BackendIsDown key="backendIsDownPage" />];
|
||||
};
|
||||
|
||||
const innerComponents = () => {
|
||||
if (backendIsUp === null) {
|
||||
return [];
|
||||
}
|
||||
if (backendIsUp) {
|
||||
return routeComponents();
|
||||
}
|
||||
return backendIsDownPage();
|
||||
};
|
||||
|
||||
return (
|
||||
<Container className={contentClassName}>
|
||||
<ScrollToTop />
|
||||
<ErrorBoundary FallbackComponent={ErrorBoundaryFallback}>
|
||||
{innerComponents()}
|
||||
</ErrorBoundary>
|
||||
</Container>
|
||||
);
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import { useErrorBoundary } from 'react-error-boundary';
|
||||
import Button from '@mui/material/Button';
|
||||
import Alert from '@mui/material/Alert';
|
||||
import AlertTitle from '@mui/material/AlertTitle';
|
||||
|
||||
type ErrorProps = {
|
||||
error: Error;
|
||||
};
|
||||
|
||||
export function ErrorBoundaryFallback({ error }: ErrorProps) {
|
||||
// This is displayed if the ErrorBoundary catches an error when rendering the form.
|
||||
const { resetBoundary } = useErrorBoundary();
|
||||
|
||||
// print the error to the console so we can debug issues
|
||||
console.error(error);
|
||||
|
||||
return (
|
||||
<Alert
|
||||
severity="error"
|
||||
action={
|
||||
<Button color="inherit" size="small" onClick={resetBoundary}>
|
||||
Try again
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<AlertTitle>Something Went Wrong.</AlertTitle>
|
||||
<p>
|
||||
We encountered an unexpected error. Please try again. If the problem
|
||||
persists, please contact your administrator.
|
||||
</p>
|
||||
<p>{error.message}</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
export default function ScrollToTop() {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, [pathname]);
|
||||
|
||||
return null;
|
||||
}
|
@ -42,10 +42,9 @@ export enum UiSchemaPersistenceLevel {
|
||||
* The arguments that can be passed in will generally match the "OwnProps" type defined within each file.
|
||||
*/
|
||||
export enum UiSchemaPageComponentList {
|
||||
// CreateNewInstance = 'CreateNewInstance',
|
||||
CustomForm = 'CustomForm',
|
||||
MarkdownRenderer = 'MarkdownRenderer',
|
||||
// ProcessInstanceListTable = 'ProcessInstanceListTable',
|
||||
ProcessInstanceListTable = 'ProcessInstanceListTable',
|
||||
ProcessInstanceRun = 'ProcessInstanceRun',
|
||||
SpiffTabs = 'SpiffTabs',
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
export default function BackendIsDown() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Server error</h1>
|
||||
<p>
|
||||
We are sorry, but our service is temporarily unavailable due to
|
||||
technical difficulties. Please bear with us while we work to resolve the
|
||||
issue. If the problem persists, please contact the site administrator.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -20,7 +20,6 @@ import Processes from './StartProcess/Processes';
|
||||
import StartProcessInstance from './StartProcess/StartProcessInstance';
|
||||
import SideNav from '../components/SideNav';
|
||||
import LoginHandler from '../components/LoginHandler';
|
||||
import Login from './Login';
|
||||
import InstancesStartedByMe from './InstancesStartedByMe';
|
||||
import TaskShow from './TaskShow/TaskShow';
|
||||
import ProcessInterstitialPage from './TaskShow/ProcessInterstitialPage';
|
||||
@ -46,12 +45,16 @@ import ReactFormEditor from './ReactFormEditor'; // Import the new component
|
||||
import ProcessInstanceRoutes from './ProcessInstanceRoutes';
|
||||
import ProcessInstanceShortLink from './ProcessInstanceShortLink';
|
||||
import ProcessInstanceList from './ProcessInstanceList'; // Import the new component
|
||||
// Import the new component
|
||||
import { UiSchemaUxElement } from '../extension_ui_schema_interfaces';
|
||||
|
||||
const fadeIn = 'fadeIn';
|
||||
const fadeOutImmediate = 'fadeOutImmediate';
|
||||
|
||||
export default function SpiffUIV3() {
|
||||
type OwnProps = {
|
||||
extensionUxElements?: UiSchemaUxElement[] | null;
|
||||
};
|
||||
|
||||
export default function BaseRoutes({ extensionUxElements }: OwnProps) {
|
||||
const storedTheme: PaletteMode = (localStorage.getItem('theme') ||
|
||||
'light') as PaletteMode;
|
||||
const [globalTheme, setGlobalTheme] = useState(
|
||||
@ -260,7 +263,6 @@ export default function SpiffUIV3() {
|
||||
path="/:modifiedProcessModelId/start"
|
||||
element={<StartProcessInstance />}
|
||||
/>
|
||||
<Route path="login" element={<Login />} />
|
||||
<Route path="/create-custom-tab" element={<ComingSoon />} />
|
||||
<Route
|
||||
path="/tasks/:process_instance_id/:task_guid"
|
@ -22,8 +22,7 @@ import ProcessInstanceRun from '../components/ProcessInstanceRun';
|
||||
import MarkdownRenderer from '../components/MarkdownRenderer';
|
||||
import LoginHandler from '../components/LoginHandler';
|
||||
import SpiffTabs from '../components/SpiffTabs';
|
||||
// import ProcessInstanceListTable from '../components/ProcessInstanceListTable';
|
||||
// import CreateNewInstance from './CreateNewInstance';
|
||||
import ProcessInstanceListTable from '../components/ProcessInstanceListTable';
|
||||
|
||||
type OwnProps = {
|
||||
pageIdentifier?: string;
|
||||
@ -63,10 +62,9 @@ export default function Extension({
|
||||
const { addError, removeError } = useAPIError();
|
||||
|
||||
const supportedComponents: SupportedComponentList = {
|
||||
// CreateNewInstance,
|
||||
CustomForm,
|
||||
MarkdownRenderer,
|
||||
// ProcessInstanceListTable,
|
||||
ProcessInstanceListTable,
|
||||
ProcessInstanceRun,
|
||||
SpiffTabs,
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user