Squashed 'spiffworkflow-frontend/' changes from 790d267b..bf2d2a22
bf2d2a22 added authentication table w/ burnettk d9d8f364 added an authentications route and updated error message to show sentry links w/ burnettk git-subtree-dir: spiffworkflow-frontend git-subtree-split: bf2d2a22430da355be261d864e3b507866deafdf
This commit is contained in:
parent
8d86fe710d
commit
75729ba3df
23
src/App.tsx
23
src/App.tsx
|
@ -10,9 +10,12 @@ import TaskShow from './routes/TaskShow';
|
||||||
import ErrorBoundary from './components/ErrorBoundary';
|
import ErrorBoundary from './components/ErrorBoundary';
|
||||||
import AdminRoutes from './routes/AdminRoutes';
|
import AdminRoutes from './routes/AdminRoutes';
|
||||||
import SubNavigation from './components/SubNavigation';
|
import SubNavigation from './components/SubNavigation';
|
||||||
|
import { ErrorForDisplay } from './interfaces';
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [errorMessage, setErrorMessage] = useState('');
|
const [errorMessage, setErrorMessage] = useState<ErrorForDisplay | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
const errorContextValueArray = useMemo(
|
const errorContextValueArray = useMemo(
|
||||||
() => [errorMessage, setErrorMessage],
|
() => [errorMessage, setErrorMessage],
|
||||||
|
@ -20,10 +23,24 @@ export default function App() {
|
||||||
);
|
);
|
||||||
|
|
||||||
let errorTag = null;
|
let errorTag = null;
|
||||||
if (errorMessage !== '') {
|
if (errorMessage) {
|
||||||
|
let sentryLinkTag = null;
|
||||||
|
if (errorMessage.sentry_link) {
|
||||||
|
sentryLinkTag = (
|
||||||
|
<span>
|
||||||
|
{
|
||||||
|
': Find details about this error here (it may take a moment to become available): '
|
||||||
|
}
|
||||||
|
<a href={errorMessage.sentry_link} target="_blank" rel="noreferrer">
|
||||||
|
{errorMessage.sentry_link}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
errorTag = (
|
errorTag = (
|
||||||
<div id="filter-errors" className="mt-4 alert alert-danger" role="alert">
|
<div id="filter-errors" className="mt-4 alert alert-danger" role="alert">
|
||||||
{errorMessage}
|
{errorMessage.message}
|
||||||
|
{sentryLinkTag}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,10 @@
|
||||||
import { Button, Navbar, Nav, Container } from 'react-bootstrap';
|
import { Button, Navbar, Nav, Container } from 'react-bootstrap';
|
||||||
// import { capitalizeFirstLetter } from '../helpers';
|
|
||||||
// @ts-expect-error TS(2307) FIXME: Cannot find module '../logo.svg' or its correspond... Remove this comment to see the full error message
|
// @ts-expect-error TS(2307) FIXME: Cannot find module '../logo.svg' or its correspond... Remove this comment to see the full error message
|
||||||
import logo from '../logo.svg';
|
import logo from '../logo.svg';
|
||||||
import UserService from '../services/UserService';
|
import UserService from '../services/UserService';
|
||||||
|
|
||||||
// for ref: https://react-bootstrap.github.io/components/navbar/
|
// for ref: https://react-bootstrap.github.io/components/navbar/
|
||||||
export default function NavigationBar() {
|
export default function NavigationBar() {
|
||||||
// const navItems: string[] = [];
|
|
||||||
// if (UserService.hasRole(['admin'])) {
|
|
||||||
// navItems.push('/admin');
|
|
||||||
// }
|
|
||||||
// navItems.push('/tasks');
|
|
||||||
//
|
|
||||||
// const navElements = navItems.map((navItem) => {
|
|
||||||
// let className = '';
|
|
||||||
// if (window.location.pathname.startsWith(navItem)) {
|
|
||||||
// className = 'active';
|
|
||||||
// }
|
|
||||||
// const navItemWithoutSlash = navItem.replace(/\/*/, '');
|
|
||||||
// const title = capitalizeFirstLetter(navItemWithoutSlash);
|
|
||||||
// return (
|
|
||||||
// <Nav.Link
|
|
||||||
// href={navItem}
|
|
||||||
// className={className}
|
|
||||||
// data-qa={`nav-item-${navItemWithoutSlash}`}
|
|
||||||
// >
|
|
||||||
// {title}
|
|
||||||
// </Nav.Link>
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
const navElements = null;
|
const navElements = null;
|
||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
|
|
|
@ -14,6 +14,8 @@ export default function SubNavigation() {
|
||||||
newActiveKey = '/admin/process-instances';
|
newActiveKey = '/admin/process-instances';
|
||||||
} else if (location.pathname.match(/^\/admin\/secrets\b/)) {
|
} else if (location.pathname.match(/^\/admin\/secrets\b/)) {
|
||||||
newActiveKey = '/admin/secrets';
|
newActiveKey = '/admin/secrets';
|
||||||
|
} else if (location.pathname.match(/^\/admin\/authentications\b/)) {
|
||||||
|
newActiveKey = '/admin/authentications';
|
||||||
} else if (location.pathname === '/') {
|
} else if (location.pathname === '/') {
|
||||||
newActiveKey = '/';
|
newActiveKey = '/';
|
||||||
} else if (location.pathname.match(/^\/tasks\b/)) {
|
} else if (location.pathname.match(/^\/tasks\b/)) {
|
||||||
|
@ -40,6 +42,9 @@ export default function SubNavigation() {
|
||||||
<Nav.Item>
|
<Nav.Item>
|
||||||
<Nav.Link href="/admin/secrets">Secrets</Nav.Link>
|
<Nav.Link href="/admin/secrets">Secrets</Nav.Link>
|
||||||
</Nav.Item>
|
</Nav.Item>
|
||||||
|
<Nav.Item>
|
||||||
|
<Nav.Link href="/admin/authentications">Authentications</Nav.Link>
|
||||||
|
</Nav.Item>
|
||||||
</Nav>
|
</Nav>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,3 +25,19 @@ export interface ProcessModel {
|
||||||
|
|
||||||
// tuple of display value and URL
|
// tuple of display value and URL
|
||||||
export type BreadcrumbItem = [displayValue: string, url?: string];
|
export type BreadcrumbItem = [displayValue: string, url?: string];
|
||||||
|
|
||||||
|
export interface ErrorForDisplay {
|
||||||
|
message: string;
|
||||||
|
sentry_link?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuthenticationParam {
|
||||||
|
id: string;
|
||||||
|
type: string;
|
||||||
|
required: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuthenticationItem {
|
||||||
|
id: string;
|
||||||
|
parameters: AuthenticationParam[];
|
||||||
|
}
|
||||||
|
|
|
@ -23,13 +23,14 @@ import MessageInstanceList from './MessageInstanceList';
|
||||||
import SecretList from './SecretList';
|
import SecretList from './SecretList';
|
||||||
import SecretNew from './SecretNew';
|
import SecretNew from './SecretNew';
|
||||||
import SecretShow from './SecretShow';
|
import SecretShow from './SecretShow';
|
||||||
|
import AuthenticationList from './AuthenticationList';
|
||||||
|
|
||||||
export default function AdminRoutes() {
|
export default function AdminRoutes() {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const setErrorMessage = (useContext as any)(ErrorContext)[1];
|
const setErrorMessage = (useContext as any)(ErrorContext)[1];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setErrorMessage('');
|
setErrorMessage(null);
|
||||||
}, [location, setErrorMessage]);
|
}, [location, setErrorMessage]);
|
||||||
|
|
||||||
if (UserService.hasRole(['admin'])) {
|
if (UserService.hasRole(['admin'])) {
|
||||||
|
@ -108,6 +109,7 @@ export default function AdminRoutes() {
|
||||||
<Route path="secrets" element={<SecretList />} />
|
<Route path="secrets" element={<SecretList />} />
|
||||||
<Route path="secrets/new" element={<SecretNew />} />
|
<Route path="secrets/new" element={<SecretNew />} />
|
||||||
<Route path="secrets/:key" element={<SecretShow />} />
|
<Route path="secrets/:key" element={<SecretShow />} />
|
||||||
|
<Route path="authentications" element={<AuthenticationList />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import { useContext, useEffect, useState } from 'react';
|
||||||
|
import { Table } from 'react-bootstrap';
|
||||||
|
import ErrorContext from '../contexts/ErrorContext';
|
||||||
|
import { AuthenticationItem } from '../interfaces';
|
||||||
|
import HttpService from '../services/HttpService';
|
||||||
|
|
||||||
|
export default function AuthenticationList() {
|
||||||
|
const setErrorMessage = (useContext as any)(ErrorContext)[1];
|
||||||
|
|
||||||
|
const [authenticationList, setAuthenticationList] = useState<
|
||||||
|
AuthenticationItem[] | null
|
||||||
|
>(null);
|
||||||
|
const [connectProxyBaseUrl, setConnectProxyBaseUrl] = useState<string | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const processResult = (result: any) => {
|
||||||
|
setAuthenticationList(result.results);
|
||||||
|
setConnectProxyBaseUrl(result.connector_proxy_base_url);
|
||||||
|
};
|
||||||
|
HttpService.makeCallToBackend({
|
||||||
|
path: `/authentications`,
|
||||||
|
successCallback: processResult,
|
||||||
|
failureCallback: setErrorMessage,
|
||||||
|
});
|
||||||
|
}, [setErrorMessage]);
|
||||||
|
|
||||||
|
const buildTable = () => {
|
||||||
|
if (authenticationList) {
|
||||||
|
const rows = authenticationList.map((row) => {
|
||||||
|
return (
|
||||||
|
<tr key={row.id}>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
data-qa="authentication-create-link"
|
||||||
|
href={`${connectProxyBaseUrl}/v1/auths/${row.id}`}
|
||||||
|
>
|
||||||
|
{row.id}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<Table striped bordered>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>{rows}</tbody>
|
||||||
|
</Table>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (authenticationList) {
|
||||||
|
return <>{buildTable()}</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <main />;
|
||||||
|
}
|
|
@ -176,19 +176,19 @@ export default function ProcessInstanceList() {
|
||||||
let queryParamString = `per_page=${perPage}&page=${page}`;
|
let queryParamString = `per_page=${perPage}&page=${page}`;
|
||||||
|
|
||||||
if (isTrueComparison(startFrom, '>', startTill)) {
|
if (isTrueComparison(startFrom, '>', startTill)) {
|
||||||
setErrorMessage('startFrom cannot be after startTill');
|
setErrorMessage({ message: 'startFrom cannot be after startTill' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isTrueComparison(endFrom, '>', endTill)) {
|
if (isTrueComparison(endFrom, '>', endTill)) {
|
||||||
setErrorMessage('endFrom cannot be after endTill');
|
setErrorMessage({ message: 'endFrom cannot be after endTill' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isTrueComparison(startFrom, '>', endFrom)) {
|
if (isTrueComparison(startFrom, '>', endFrom)) {
|
||||||
setErrorMessage('startFrom cannot be after endFrom');
|
setErrorMessage({ message: 'startFrom cannot be after endFrom' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isTrueComparison(startTill, '>', endTill)) {
|
if (isTrueComparison(startTill, '>', endTill)) {
|
||||||
setErrorMessage('startTill cannot be after endTill');
|
setErrorMessage({ message: 'startTill cannot be after endTill' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +217,7 @@ export default function ProcessInstanceList() {
|
||||||
queryParamString += `&process_group_identifier=${currentProcessModel.process_group_id}&process_model_identifier=${currentProcessModel.id}`;
|
queryParamString += `&process_group_identifier=${currentProcessModel.process_group_id}&process_model_identifier=${currentProcessModel.id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrorMessage('');
|
setErrorMessage(null);
|
||||||
navigate(`/admin/process-instances?${queryParamString}`);
|
navigate(`/admin/process-instances?${queryParamString}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ export default function ProcessModelEdit() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteProcessModel = () => {
|
const deleteProcessModel = () => {
|
||||||
setErrorMessage('');
|
setErrorMessage(null);
|
||||||
const processModelToUse = processModel as any;
|
const processModelToUse = processModel as any;
|
||||||
const processModelShowPath = `/process-models/${processModelToUse.process_group_id}/${processModelToUse.id}`;
|
const processModelShowPath = `/process-models/${processModelToUse.process_group_id}/${processModelToUse.id}`;
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
|
|
|
@ -112,7 +112,7 @@ export default function ProcessModelEditDiagram() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveDiagram = (bpmnXML: any, fileName = params.file_name) => {
|
const saveDiagram = (bpmnXML: any, fileName = params.file_name) => {
|
||||||
setErrorMessage('');
|
setErrorMessage(null);
|
||||||
setBpmnXmlForDiagramRendering(bpmnXML);
|
setBpmnXmlForDiagramRendering(bpmnXML);
|
||||||
|
|
||||||
let url = `/process-models/${params.process_group_id}/${params.process_model_id}/files`;
|
let url = `/process-models/${params.process_group_id}/${params.process_model_id}/files`;
|
||||||
|
|
|
@ -82,7 +82,7 @@ export default function ProcessModelShow() {
|
||||||
}, [params, reloadModel]);
|
}, [params, reloadModel]);
|
||||||
|
|
||||||
const processModelRun = (processInstance: any) => {
|
const processModelRun = (processInstance: any) => {
|
||||||
setErrorMessage('');
|
setErrorMessage(null);
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
path: `/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/${processInstance.id}/run`,
|
path: `/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/${processInstance.id}/run`,
|
||||||
successCallback: setProcessInstanceResult,
|
successCallback: setProcessInstanceResult,
|
||||||
|
|
|
@ -29,7 +29,7 @@ export default function TaskShow() {
|
||||||
}, [params, setErrorMessage]);
|
}, [params, setErrorMessage]);
|
||||||
|
|
||||||
const processSubmitResult = (result: any) => {
|
const processSubmitResult = (result: any) => {
|
||||||
setErrorMessage('');
|
setErrorMessage(null);
|
||||||
if (result.ok) {
|
if (result.ok) {
|
||||||
navigate(`/tasks`);
|
navigate(`/tasks`);
|
||||||
} else if (result.process_instance_id) {
|
} else if (result.process_instance_id) {
|
||||||
|
@ -40,7 +40,7 @@ export default function TaskShow() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFormSubmit = (event: any) => {
|
const handleFormSubmit = (event: any) => {
|
||||||
setErrorMessage('');
|
setErrorMessage(null);
|
||||||
const dataToSubmit = event.formData;
|
const dataToSubmit = event.formData;
|
||||||
delete dataToSubmit.isManualTask;
|
delete dataToSubmit.isManualTask;
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
|
|
|
@ -94,7 +94,7 @@ backendCallProps) => {
|
||||||
message = result.message;
|
message = result.message;
|
||||||
}
|
}
|
||||||
if (failureCallback) {
|
if (failureCallback) {
|
||||||
failureCallback(message);
|
failureCallback(result);
|
||||||
} else {
|
} else {
|
||||||
console.error(message);
|
console.error(message);
|
||||||
// eslint-disable-next-line no-alert
|
// eslint-disable-next-line no-alert
|
||||||
|
@ -105,7 +105,7 @@ backendCallProps) => {
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
if (error.name !== 'UnauthenticatedError') {
|
if (error.name !== 'UnauthenticatedError') {
|
||||||
if (failureCallback) {
|
if (failureCallback) {
|
||||||
failureCallback(error.message);
|
failureCallback(error);
|
||||||
} else {
|
} else {
|
||||||
console.error(error.message);
|
console.error(error.message);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue