Skip to content

Commit

Permalink
Converting Spliff to microfront end
Browse files Browse the repository at this point in the history
  • Loading branch information
ratheesh-aot committed Jul 17, 2024
1 parent 254e3e7 commit 678cc4f
Show file tree
Hide file tree
Showing 13 changed files with 483 additions and 8 deletions.
61 changes: 61 additions & 0 deletions spiffworkflow-frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions spiffworkflow-frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,13 @@
},
"scripts": {
"build": "vite build",
"build-micro": "vite build --base=/spliff --config vite.micro.config.ts --mode production",
"eslint": "./node_modules/.bin/eslint src --ext .js,.jsx,.ts,.tsx",
"format": "prettier --write src/**/*.[tj]s{,x}",
"lint": "npm run eslint && npm run typecheck",
"lint:fix": "./node_modules/.bin/eslint --fix src --ext .js,.jsx,.ts,.tsx",
"serve": "vite preview",
"start": "VITE_VERSION_INFO='{\"version\":\"local\"}' vite",
"start": "vite",
"test": "vitest run --coverage",
"typecheck": "./node_modules/.bin/tsc --noEmit"
},
Expand Down Expand Up @@ -114,6 +115,7 @@
"cypress-slow-down": "^1.3.1",
"cypress-vite": "^1.5.0",
"eslint": "^8.56.0",
"eslint_d": "^12.2.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-cypress": "^3.3.0",
Expand All @@ -124,14 +126,15 @@
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-sonarjs": "^1.0.3",
"eslint-plugin-unused-imports": "^3.2.0",
"eslint_d": "^12.2.0",
"inherits-browser": "^0.0.1",
"jsdom": "^24.0.0",
"nice-select2": "^2.1.0",
"prettier": "^3.3.2",
"safe-regex": "^2.1.1",
"single-spa-react": "^6.0.1",
"tiny-svg": "^2.2.3",
"ts-migrate": "^0.1.30",
"vite-plugin-single-spa": "^0.7.0",
"vitest": "^1.5.0"
}
}
55 changes: 55 additions & 0 deletions spiffworkflow-frontend/src/AppSpa.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { defineAbility } from '@casl/ability';

import { createBrowserRouter, Outlet, RouterProvider } from 'react-router-dom';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { AbilityContext } from './contexts/Can';
import APIErrorProvider from './contexts/APIErrorContext';
import ContainerForExtensionsMicro from './ContainerForExtensionsMicro';
import { BASENAME_URL } from './config';

const queryClient = new QueryClient();

export default function AppSpa() {
const ability = defineAbility(() => {});
const routeComponents = () => {
return [
{
path: '*',
element: <ContainerForExtensionsMicro />,
},
];
};

/**
* Note that QueryClientProvider and ReactQueryDevTools
* are React Qery, now branded under the Tanstack packages.
* https://tanstack.com/query/latest
*/
const layout = () => {
return (
<div className="cds--white">
<QueryClientProvider client={queryClient}>
<APIErrorProvider>
<AbilityContext.Provider value={ability}>
<Outlet />
<ReactQueryDevtools initialIsOpen={false} />
</AbilityContext.Provider>
</APIErrorProvider>
</QueryClientProvider>
</div>
);
};
const router = createBrowserRouter([
{
path: '*',
Component: layout,
children: routeComponents(),
},
],
{
basename: BASENAME_URL
}
);
return <RouterProvider router={router} />;
}
161 changes: 161 additions & 0 deletions spiffworkflow-frontend/src/ContainerForExtensionsMicro.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { Content } from '@carbon/react';
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 EditorRoutes from './routes/EditorRoutes';
import Extension from './routes/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 './routes/BaseRoutes';
import BaseRoutesMicro from './routes/BaseRoutesMicro';
import BackendIsDown from './routes/BackendIsDown';
import Login from './routes/Login';
import NavigationBar from './components/NavigationBar';
import useAPIError from './hooks/UseApiError';

export default function ContainerForExtensionsMicro() {
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={<BaseRoutesMicro extensionUxElements={extensionUxElements} />}
/>
<Route path="editor/*" element={<EditorRoutes />} />
<Route path="extensions/:page_identifier" element={<Extension />} />
<Route path="login" element={<Login />} />
</Routes>
);
};

const backendIsDownPage = () => {
return [<BackendIsDown />];
};

const innerComponents = () => {
if (backendIsUp === null) {
return [];
}
if (backendIsUp) {
return routeComponents();
}
return backendIsDownPage();
};

return (
<>
{/* TODO : remove this NavigationBar when we have a new navigation system */}
<div className="hidden">
<NavigationBar extensionUxElements={extensionUxElements} />
</div>

<Content className={contentClassName}>
<ScrollToTop />
<ErrorBoundary FallbackComponent={ErrorBoundaryFallback}>
{innerComponents()}
</ErrorBoundary>
</Content>
</>
);
}
3 changes: 2 additions & 1 deletion spiffworkflow-frontend/src/components/LoginHandler.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import UserService from '../services/UserService';
import { BASENAME_URL } from '../config';

export default function LoginHandler() {
const navigate = useNavigate();
useEffect(() => {
if (!UserService.isLoggedIn()) {
navigate(`/login?original_url=${UserService.getCurrentLocation()}`);
navigate(BASENAME_URL+ `/login?original_url=${UserService.getCurrentLocation()}`);
}
}, [navigate]);
return null;
Expand Down
Loading

0 comments on commit 678cc4f

Please sign in to comment.