mirror of
https://github.com/sartography/spiff-arena.git
synced 2025-02-23 06:38:24 +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 { AbilityContext } from './contexts/Can';
|
||||||
import APIErrorProvider from './contexts/APIErrorContext';
|
import APIErrorProvider from './contexts/APIErrorContext';
|
||||||
import ContainerForExtensions from './ContainerForExtensions';
|
import ContainerForExtensions from './ContainerForExtensions';
|
||||||
|
import ContainerForExtensionsV3 from './a-spiffui-v3/ContainerForExtensions';
|
||||||
import PublicRoutes from './a-spiffui-v3/views/PublicRoutes';
|
import PublicRoutes from './a-spiffui-v3/views/PublicRoutes';
|
||||||
import SpiffUIV3 from './a-spiffui-v3/views/SpiffUIV3';
|
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ export default function App() {
|
|||||||
{ path: 'public/*', element: <PublicRoutes /> },
|
{ path: 'public/*', element: <PublicRoutes /> },
|
||||||
{
|
{
|
||||||
path: 'newui/*',
|
path: 'newui/*',
|
||||||
element: <SpiffUIV3 />,
|
element: <ContainerForExtensionsV3 />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '*',
|
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.
|
* The arguments that can be passed in will generally match the "OwnProps" type defined within each file.
|
||||||
*/
|
*/
|
||||||
export enum UiSchemaPageComponentList {
|
export enum UiSchemaPageComponentList {
|
||||||
// CreateNewInstance = 'CreateNewInstance',
|
|
||||||
CustomForm = 'CustomForm',
|
CustomForm = 'CustomForm',
|
||||||
MarkdownRenderer = 'MarkdownRenderer',
|
MarkdownRenderer = 'MarkdownRenderer',
|
||||||
// ProcessInstanceListTable = 'ProcessInstanceListTable',
|
ProcessInstanceListTable = 'ProcessInstanceListTable',
|
||||||
ProcessInstanceRun = 'ProcessInstanceRun',
|
ProcessInstanceRun = 'ProcessInstanceRun',
|
||||||
SpiffTabs = 'SpiffTabs',
|
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 StartProcessInstance from './StartProcess/StartProcessInstance';
|
||||||
import SideNav from '../components/SideNav';
|
import SideNav from '../components/SideNav';
|
||||||
import LoginHandler from '../components/LoginHandler';
|
import LoginHandler from '../components/LoginHandler';
|
||||||
import Login from './Login';
|
|
||||||
import InstancesStartedByMe from './InstancesStartedByMe';
|
import InstancesStartedByMe from './InstancesStartedByMe';
|
||||||
import TaskShow from './TaskShow/TaskShow';
|
import TaskShow from './TaskShow/TaskShow';
|
||||||
import ProcessInterstitialPage from './TaskShow/ProcessInterstitialPage';
|
import ProcessInterstitialPage from './TaskShow/ProcessInterstitialPage';
|
||||||
@ -46,12 +45,16 @@ import ReactFormEditor from './ReactFormEditor'; // Import the new component
|
|||||||
import ProcessInstanceRoutes from './ProcessInstanceRoutes';
|
import ProcessInstanceRoutes from './ProcessInstanceRoutes';
|
||||||
import ProcessInstanceShortLink from './ProcessInstanceShortLink';
|
import ProcessInstanceShortLink from './ProcessInstanceShortLink';
|
||||||
import ProcessInstanceList from './ProcessInstanceList'; // Import the new component
|
import ProcessInstanceList from './ProcessInstanceList'; // Import the new component
|
||||||
// Import the new component
|
import { UiSchemaUxElement } from '../extension_ui_schema_interfaces';
|
||||||
|
|
||||||
const fadeIn = 'fadeIn';
|
const fadeIn = 'fadeIn';
|
||||||
const fadeOutImmediate = 'fadeOutImmediate';
|
const fadeOutImmediate = 'fadeOutImmediate';
|
||||||
|
|
||||||
export default function SpiffUIV3() {
|
type OwnProps = {
|
||||||
|
extensionUxElements?: UiSchemaUxElement[] | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function BaseRoutes({ extensionUxElements }: OwnProps) {
|
||||||
const storedTheme: PaletteMode = (localStorage.getItem('theme') ||
|
const storedTheme: PaletteMode = (localStorage.getItem('theme') ||
|
||||||
'light') as PaletteMode;
|
'light') as PaletteMode;
|
||||||
const [globalTheme, setGlobalTheme] = useState(
|
const [globalTheme, setGlobalTheme] = useState(
|
||||||
@ -260,7 +263,6 @@ export default function SpiffUIV3() {
|
|||||||
path="/:modifiedProcessModelId/start"
|
path="/:modifiedProcessModelId/start"
|
||||||
element={<StartProcessInstance />}
|
element={<StartProcessInstance />}
|
||||||
/>
|
/>
|
||||||
<Route path="login" element={<Login />} />
|
|
||||||
<Route path="/create-custom-tab" element={<ComingSoon />} />
|
<Route path="/create-custom-tab" element={<ComingSoon />} />
|
||||||
<Route
|
<Route
|
||||||
path="/tasks/:process_instance_id/:task_guid"
|
path="/tasks/:process_instance_id/:task_guid"
|
@ -22,8 +22,7 @@ import ProcessInstanceRun from '../components/ProcessInstanceRun';
|
|||||||
import MarkdownRenderer from '../components/MarkdownRenderer';
|
import MarkdownRenderer from '../components/MarkdownRenderer';
|
||||||
import LoginHandler from '../components/LoginHandler';
|
import LoginHandler from '../components/LoginHandler';
|
||||||
import SpiffTabs from '../components/SpiffTabs';
|
import SpiffTabs from '../components/SpiffTabs';
|
||||||
// import ProcessInstanceListTable from '../components/ProcessInstanceListTable';
|
import ProcessInstanceListTable from '../components/ProcessInstanceListTable';
|
||||||
// import CreateNewInstance from './CreateNewInstance';
|
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
pageIdentifier?: string;
|
pageIdentifier?: string;
|
||||||
@ -63,10 +62,9 @@ export default function Extension({
|
|||||||
const { addError, removeError } = useAPIError();
|
const { addError, removeError } = useAPIError();
|
||||||
|
|
||||||
const supportedComponents: SupportedComponentList = {
|
const supportedComponents: SupportedComponentList = {
|
||||||
// CreateNewInstance,
|
|
||||||
CustomForm,
|
CustomForm,
|
||||||
MarkdownRenderer,
|
MarkdownRenderer,
|
||||||
// ProcessInstanceListTable,
|
ProcessInstanceListTable,
|
||||||
ProcessInstanceRun,
|
ProcessInstanceRun,
|
||||||
SpiffTabs,
|
SpiffTabs,
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user