made activeuser component which can be used wherever and only use it if the user can save the file on the form and diagram edit pages
This commit is contained in:
parent
863350bdb6
commit
7be5bf43fd
|
@ -0,0 +1,52 @@
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import HttpService from '../services/HttpService';
|
||||||
|
import {
|
||||||
|
encodeBase64,
|
||||||
|
refreshAtInterval,
|
||||||
|
REFRESH_TIMEOUT_SECONDS,
|
||||||
|
} from '../helpers';
|
||||||
|
import { User } from '../interfaces';
|
||||||
|
|
||||||
|
export default function ActiveUsers() {
|
||||||
|
// Handles getting and displaying active users.
|
||||||
|
const [activeUsers, setActiveUsers] = useState<User[]>([]);
|
||||||
|
|
||||||
|
const lastVisitedIdentifier = encodeBase64(window.location.pathname);
|
||||||
|
useEffect(() => {
|
||||||
|
const updateActiveUsers = () => {
|
||||||
|
HttpService.makeCallToBackend({
|
||||||
|
path: `/active-users/updates/${lastVisitedIdentifier}`,
|
||||||
|
successCallback: setActiveUsers,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const unregisterUser = () => {
|
||||||
|
HttpService.makeCallToBackend({
|
||||||
|
path: `/active-users/unregister/${lastVisitedIdentifier}`,
|
||||||
|
successCallback: setActiveUsers,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
updateActiveUsers();
|
||||||
|
|
||||||
|
return refreshAtInterval(
|
||||||
|
15,
|
||||||
|
REFRESH_TIMEOUT_SECONDS,
|
||||||
|
updateActiveUsers,
|
||||||
|
unregisterUser
|
||||||
|
);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []); // it is critical to only run this once.
|
||||||
|
|
||||||
|
const au = activeUsers.map((activeUser: User) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
title={`${activeUser.username} is also viewing this page`}
|
||||||
|
className="user-circle"
|
||||||
|
>
|
||||||
|
{activeUser.username.charAt(0).toUpperCase()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return <div className="user-list">{au}</div>;
|
||||||
|
}
|
|
@ -674,7 +674,14 @@ export default function ReactDiagramEditor({
|
||||||
)}
|
)}
|
||||||
</Can>
|
</Can>
|
||||||
{getReferencesButton()}
|
{getReferencesButton()}
|
||||||
{activeUserElement || null}
|
{/* only show other users if the current user can save the current diagram */}
|
||||||
|
<Can
|
||||||
|
I="PUT"
|
||||||
|
a={targetUris.processModelFileShowPath}
|
||||||
|
ability={ability}
|
||||||
|
>
|
||||||
|
{activeUserElement || null}
|
||||||
|
</Can>
|
||||||
</ButtonSet>
|
</ButtonSet>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,24 +29,18 @@ import HttpService from '../services/HttpService';
|
||||||
import ReactDiagramEditor from '../components/ReactDiagramEditor';
|
import ReactDiagramEditor from '../components/ReactDiagramEditor';
|
||||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||||
import useAPIError from '../hooks/UseApiError';
|
import useAPIError from '../hooks/UseApiError';
|
||||||
import {
|
import { makeid, modifyProcessIdentifierForPathParam } from '../helpers';
|
||||||
makeid,
|
|
||||||
modifyProcessIdentifierForPathParam,
|
|
||||||
encodeBase64,
|
|
||||||
refreshAtInterval,
|
|
||||||
REFRESH_TIMEOUT_SECONDS,
|
|
||||||
} from '../helpers';
|
|
||||||
import {
|
import {
|
||||||
CarbonComboBoxProcessSelection,
|
CarbonComboBoxProcessSelection,
|
||||||
ProcessFile,
|
ProcessFile,
|
||||||
ProcessModel,
|
ProcessModel,
|
||||||
ProcessModelCaller,
|
ProcessModelCaller,
|
||||||
ProcessReference,
|
ProcessReference,
|
||||||
User,
|
|
||||||
} from '../interfaces';
|
} from '../interfaces';
|
||||||
import ProcessSearch from '../components/ProcessSearch';
|
import ProcessSearch from '../components/ProcessSearch';
|
||||||
import { Notification } from '../components/Notification';
|
import { Notification } from '../components/Notification';
|
||||||
import { usePrompt } from '../hooks/UsePrompt';
|
import { usePrompt } from '../hooks/UsePrompt';
|
||||||
|
import ActiveUsers from '../components/ActiveUsers';
|
||||||
|
|
||||||
export default function ProcessModelEditDiagram() {
|
export default function ProcessModelEditDiagram() {
|
||||||
const [showFileNameEditor, setShowFileNameEditor] = useState(false);
|
const [showFileNameEditor, setShowFileNameEditor] = useState(false);
|
||||||
|
@ -73,7 +67,6 @@ export default function ProcessModelEditDiagram() {
|
||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
const [processModelFileInvalidText, setProcessModelFileInvalidText] =
|
const [processModelFileInvalidText, setProcessModelFileInvalidText] =
|
||||||
useState<string>('');
|
useState<string>('');
|
||||||
const [activeUsers, setActiveUsers] = useState<User[]>([]);
|
|
||||||
|
|
||||||
const handleShowMarkdownEditor = () => setShowMarkdownEditor(true);
|
const handleShowMarkdownEditor = () => setShowMarkdownEditor(true);
|
||||||
|
|
||||||
|
@ -132,14 +125,7 @@ export default function ProcessModelEditDiagram() {
|
||||||
|
|
||||||
usePrompt('Changes you made may not be saved.', diagramHasChanges);
|
usePrompt('Changes you made may not be saved.', diagramHasChanges);
|
||||||
|
|
||||||
const lastVisitedIdentifier = encodeBase64(window.location.pathname);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const updateActiveUsers = () => {
|
|
||||||
HttpService.makeCallToBackend({
|
|
||||||
path: `/active-users/updates/${lastVisitedIdentifier}`,
|
|
||||||
successCallback: setActiveUsers,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
// Grab all available process models in case we need to search for them.
|
// Grab all available process models in case we need to search for them.
|
||||||
// Taken from the Process Group List
|
// Taken from the Process Group List
|
||||||
const processResults = (result: any) => {
|
const processResults = (result: any) => {
|
||||||
|
@ -154,21 +140,6 @@ export default function ProcessModelEditDiagram() {
|
||||||
path: `/processes`,
|
path: `/processes`,
|
||||||
successCallback: processResults,
|
successCallback: processResults,
|
||||||
});
|
});
|
||||||
|
|
||||||
const unregisterUser = () => {
|
|
||||||
HttpService.makeCallToBackend({
|
|
||||||
path: `/active-users/unregister/${lastVisitedIdentifier}`,
|
|
||||||
successCallback: setActiveUsers,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
updateActiveUsers();
|
|
||||||
|
|
||||||
return refreshAtInterval(
|
|
||||||
15,
|
|
||||||
REFRESH_TIMEOUT_SECONDS,
|
|
||||||
updateActiveUsers,
|
|
||||||
unregisterUser
|
|
||||||
);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []); // it is critical to only run this once.
|
}, []); // it is critical to only run this once.
|
||||||
|
|
||||||
|
@ -959,20 +930,6 @@ export default function ProcessModelEditDiagram() {
|
||||||
return searchParams.get('file_type') === 'dmn' || fileName.endsWith('.dmn');
|
return searchParams.get('file_type') === 'dmn' || fileName.endsWith('.dmn');
|
||||||
};
|
};
|
||||||
|
|
||||||
const activeUserElement = () => {
|
|
||||||
const au = activeUsers.map((activeUser: User) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
title={`${activeUser.username} is also viewing this page`}
|
|
||||||
className="user-circle"
|
|
||||||
>
|
|
||||||
{activeUser.username.charAt(0).toUpperCase()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return <div className="user-list">{au}</div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const appropriateEditor = () => {
|
const appropriateEditor = () => {
|
||||||
if (isDmn()) {
|
if (isDmn()) {
|
||||||
return (
|
return (
|
||||||
|
@ -1017,7 +974,7 @@ export default function ProcessModelEditDiagram() {
|
||||||
onSearchProcessModels={onSearchProcessModels}
|
onSearchProcessModels={onSearchProcessModels}
|
||||||
onElementsChanged={onElementsChanged}
|
onElementsChanged={onElementsChanged}
|
||||||
callers={callers}
|
callers={callers}
|
||||||
activeUserElement={activeUserElement()}
|
activeUserElement={<ActiveUsers />}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useEffect, useState } from 'react';
|
||||||
import Editor from '@monaco-editor/react';
|
import Editor from '@monaco-editor/react';
|
||||||
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { Button, Modal } from '@carbon/react';
|
import { Button, ButtonSet, Modal } from '@carbon/react';
|
||||||
import { Can } from '@casl/react';
|
import { Can } from '@casl/react';
|
||||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||||
import HttpService from '../services/HttpService';
|
import HttpService from '../services/HttpService';
|
||||||
|
@ -15,6 +15,7 @@ import { Notification } from '../components/Notification';
|
||||||
import useAPIError from '../hooks/UseApiError';
|
import useAPIError from '../hooks/UseApiError';
|
||||||
import { usePermissionFetcher } from '../hooks/PermissionService';
|
import { usePermissionFetcher } from '../hooks/PermissionService';
|
||||||
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
||||||
|
import ActiveUsers from '../components/ActiveUsers';
|
||||||
// NOTE: This is mostly the same as ProcessModelEditDiagram and if we go this route could
|
// NOTE: This is mostly the same as ProcessModelEditDiagram and if we go this route could
|
||||||
// possibly be merged into it. I'm leaving as a separate file now in case it does
|
// possibly be merged into it. I'm leaving as a separate file now in case it does
|
||||||
// end up diverging greatly
|
// end up diverging greatly
|
||||||
|
@ -231,59 +232,80 @@ export default function ReactFormEditor() {
|
||||||
{newFileNameBox()}
|
{newFileNameBox()}
|
||||||
{saveFileMessage()}
|
{saveFileMessage()}
|
||||||
|
|
||||||
<Can I="PUT" a={targetUris.processModelFileShowPath} ability={ability}>
|
<ButtonSet>
|
||||||
<Button
|
<Can
|
||||||
onClick={saveFile}
|
I="PUT"
|
||||||
variant="danger"
|
a={targetUris.processModelFileShowPath}
|
||||||
data-qa="file-save-button"
|
ability={ability}
|
||||||
>
|
>
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
</Can>
|
|
||||||
<Can
|
|
||||||
I="DELETE"
|
|
||||||
a={targetUris.processModelFileShowPath}
|
|
||||||
ability={ability}
|
|
||||||
>
|
|
||||||
{params.file_name ? (
|
|
||||||
<ButtonWithConfirmation
|
|
||||||
data-qa="delete-process-model-file"
|
|
||||||
description={`Delete file ${params.file_name}?`}
|
|
||||||
onConfirmation={deleteFile}
|
|
||||||
buttonLabel="Delete"
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</Can>
|
|
||||||
<Can I="PUT" a={targetUris.processModelFileShowPath} ability={ability}>
|
|
||||||
{hasFormBuilder ? (
|
|
||||||
<Button
|
<Button
|
||||||
onClick={() =>
|
onClick={saveFile}
|
||||||
navigate(
|
|
||||||
`/admin/process-models/${params.process_model_id}/form-builder${formBuildFileParam}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
variant="danger"
|
variant="danger"
|
||||||
data-qa="form-builder-button"
|
data-qa="file-save-button"
|
||||||
>
|
>
|
||||||
Form Builder
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
) : null}
|
</Can>
|
||||||
</Can>
|
<Can
|
||||||
<Can I="GET" a={targetUris.processModelFileShowPath} ability={ability}>
|
I="DELETE"
|
||||||
{hasDiagram ? (
|
a={targetUris.processModelFileShowPath}
|
||||||
<Button
|
ability={ability}
|
||||||
onClick={() =>
|
>
|
||||||
navigate(
|
{params.file_name ? (
|
||||||
`/admin/process-models/${modifiedProcessModelId}/files/${params.file_name}`
|
<ButtonWithConfirmation
|
||||||
)
|
data-qa="delete-process-model-file"
|
||||||
}
|
description={`Delete file ${params.file_name}?`}
|
||||||
variant="danger"
|
onConfirmation={deleteFile}
|
||||||
data-qa="view-diagram-button"
|
buttonLabel="Delete"
|
||||||
>
|
/>
|
||||||
View Diagram
|
) : null}
|
||||||
</Button>
|
</Can>
|
||||||
) : null}
|
<Can
|
||||||
</Can>
|
I="PUT"
|
||||||
|
a={targetUris.processModelFileShowPath}
|
||||||
|
ability={ability}
|
||||||
|
>
|
||||||
|
{hasFormBuilder ? (
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
navigate(
|
||||||
|
`/admin/process-models/${params.process_model_id}/form-builder${formBuildFileParam}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
variant="danger"
|
||||||
|
data-qa="form-builder-button"
|
||||||
|
>
|
||||||
|
Form Builder
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</Can>
|
||||||
|
<Can
|
||||||
|
I="GET"
|
||||||
|
a={targetUris.processModelFileShowPath}
|
||||||
|
ability={ability}
|
||||||
|
>
|
||||||
|
{hasDiagram ? (
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
navigate(
|
||||||
|
`/admin/process-models/${modifiedProcessModelId}/files/${params.file_name}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
variant="danger"
|
||||||
|
data-qa="view-diagram-button"
|
||||||
|
>
|
||||||
|
View Diagram
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</Can>
|
||||||
|
<Can
|
||||||
|
I="PUT"
|
||||||
|
a={targetUris.processModelFileShowPath}
|
||||||
|
ability={ability}
|
||||||
|
>
|
||||||
|
<ActiveUsers />
|
||||||
|
</Can>
|
||||||
|
</ButtonSet>
|
||||||
<Editor
|
<Editor
|
||||||
height={600}
|
height={600}
|
||||||
width="auto"
|
width="auto"
|
||||||
|
|
Loading…
Reference in New Issue