Squashed 'spiffworkflow-frontend/' changes from 572d779f7..d68d521d7
d68d521d7 also alert for unexpected errors from the api result w/ burnettk 698f7595a show an alert if backend gives a 403 w/ burnettk fa65efbb3 Merge branch 'main' of github.com:sartography/spiffworkflow-frontend f2a629110 do not null out error message in useEffect to avoid endless redirects w/ burnettk b5b79bae7 Remove the AllowedModel stuff from secrets Clean up spacing on SecretNew Don't allow spaces in Secret key. Change them to dashes. 9ab1da07f Merge branch 'main' into feature/secrets 4936296bd add link for path column too 4272ad5ac No slugify, too extreme for this use case d5cdfed18 Merge remote-tracking branch 'origin/main' into feature/secrets 4a81b2a10 add SecretAllowedModelEdit 87f640e3a Don't allow spaces on secret keys c627ef9e8 Don't display value on SecretList d42bd4ced fix method shorthand 82340ddc2 commit for KB a2c210a5a First stab at edit 1a2646249 fix typo - allowed_process_models -> allowed_processes 519555919 add interface for allowed SecretAllowedProcessModel, and use it in Secret interface use Secret and SecretAllowedProcessModel interfaces instead of `any` in SecretShow 6ccc7dd31 Merge branch 'main' into feature/secrets accd8e364 Moved button to bottom Added Secrets heading 18b80556a delete allowed model path 2dfe4c41a remove unused code df006b2af Rename Allowed Process Path to Allowed Model 8a68de12a add SecretAllowedModelNew eadf54357 Merge branch 'main' into feature/secrets f016b90b8 Cleaning up delete 7e5db93f0 Committing so the machine can look at an error 8655e437d Delete 2 ways -- icon and button w/icon ce537d7e8 display list of allowed processes skeleton of delete using MdDelete icon ab19657c0 install react-icons git-subtree-dir: spiffworkflow-frontend git-subtree-split: d68d521d78f747d156cfd45e6a7b26f4a5f5d63b
This commit is contained in:
parent
3cfc1bd383
commit
a087961fab
|
@ -43,6 +43,7 @@
|
|||
"react-bootstrap-typeahead": "^6.0.0",
|
||||
"react-datepicker": "^4.8.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^4.4.0",
|
||||
"react-jsonschema-form": "^1.8.1",
|
||||
"react-markdown": "^8.0.3",
|
||||
"react-router-dom": "^6.3.0",
|
||||
|
@ -22404,6 +22405,14 @@
|
|||
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
|
||||
"integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
|
||||
},
|
||||
"node_modules/react-icons": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.4.0.tgz",
|
||||
"integrity": "sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg==",
|
||||
"peerDependencies": {
|
||||
"react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "16.9.0",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz",
|
||||
|
@ -44975,7 +44984,7 @@
|
|||
"@csstools/postcss-text-decoration-shorthand": "^1.0.0",
|
||||
"@csstools/postcss-trigonometric-functions": "^1.0.2",
|
||||
"@csstools/postcss-unset-value": "^1.0.2",
|
||||
"autoprefixer": "10.4.8",
|
||||
"autoprefixer": "10.4.5",
|
||||
"browserslist": "^4.21.3",
|
||||
"css-blank-pseudo": "^3.0.3",
|
||||
"css-has-pseudo": "^3.0.4",
|
||||
|
@ -45013,7 +45022,8 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"autoprefixer": {
|
||||
"version": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.5.tgz",
|
||||
"version": "10.4.5",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.5.tgz",
|
||||
"integrity": "sha512-Fvd8yCoA7lNX/OUllvS+aS1I7WRBclGXsepbvT8ZaPgrH24rgXpZzF0/6Hh3ZEkwg+0AES/Osd196VZmYoEFtw==",
|
||||
"requires": {
|
||||
"browserslist": "^4.20.2",
|
||||
|
@ -45721,6 +45731,12 @@
|
|||
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
|
||||
"integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
|
||||
},
|
||||
"react-icons": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.4.0.tgz",
|
||||
"integrity": "sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg==",
|
||||
"requires": {}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.9.0",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz",
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
"react-bootstrap-typeahead": "^6.0.0",
|
||||
"react-datepicker": "^4.8.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^4.4.0",
|
||||
"react-jsonschema-form": "^1.8.1",
|
||||
"react-markdown": "^8.0.3",
|
||||
"react-router-dom": "^6.3.0",
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
export interface Secret {
|
||||
id: number;
|
||||
key: string;
|
||||
value: string;
|
||||
username: string;
|
||||
creator_user_id: string;
|
||||
}
|
||||
|
||||
export interface RecentProcessModel {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { Link, useSearchParams } from 'react-router-dom';
|
||||
import { Button, Table } from 'react-bootstrap';
|
||||
import { MdDelete } from 'react-icons/md';
|
||||
import PaginationForTable from '../components/PaginationForTable';
|
||||
import HttpService from '../services/HttpService';
|
||||
import { getPageInfoFromSearchParams } from '../helpers';
|
||||
|
@ -23,17 +24,36 @@ export default function SecretList() {
|
|||
});
|
||||
}, [searchParams]);
|
||||
|
||||
const reloadSecrets = (_result: any) => {
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
const handleDeleteSecret = (key: any) => {
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/secrets/${key}`,
|
||||
successCallback: reloadSecrets,
|
||||
httpMethod: 'DELETE',
|
||||
});
|
||||
};
|
||||
|
||||
const buildTable = () => {
|
||||
const rows = secrets.map((row) => {
|
||||
return (
|
||||
<tr key={(row as any).key}>
|
||||
<td>
|
||||
<Link to={`/admin/secrets/${(row as any).key}`}>
|
||||
{(row as any).id}
|
||||
</Link>
|
||||
</td>
|
||||
<td>
|
||||
<Link to={`/admin/secrets/${(row as any).key}`}>
|
||||
{(row as any).key}
|
||||
</Link>
|
||||
</td>
|
||||
<td>{(row as any).value}</td>
|
||||
<td>{(row as any).username}</td>
|
||||
<td>
|
||||
<MdDelete onClick={() => handleDeleteSecret((row as any).key)} />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
|
@ -41,9 +61,10 @@ export default function SecretList() {
|
|||
<Table striped bordered>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Secret Key</th>
|
||||
<th>Secret Value</th>
|
||||
<th>Creator</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{rows}</tbody>
|
||||
|
@ -72,12 +93,11 @@ export default function SecretList() {
|
|||
|
||||
if (pagination) {
|
||||
return (
|
||||
<>
|
||||
<Button href="/admin/secrets/new">Add a secret</Button>
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<h2>Secrets</h2>
|
||||
{SecretsDisplayArea()}
|
||||
</>
|
||||
<Button href="/admin/secrets/new">Add a secret</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Stack } from 'react-bootstrap';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import Form from 'react-bootstrap/Form';
|
||||
import HttpService from '../services/HttpService';
|
||||
|
@ -13,6 +14,18 @@ export default function SecretNew() {
|
|||
navigate(`/admin/secrets/${key}`);
|
||||
};
|
||||
|
||||
const navigateToSecrets = () => {
|
||||
navigate(`/admin/secrets`);
|
||||
};
|
||||
|
||||
const changeSpacesToDash = (someString: string) => {
|
||||
// change spaces to `-`
|
||||
let s1 = someString.replace(' ', '-');
|
||||
// remove any trailing `-`
|
||||
s1 = s1.replace(/-$/, '');
|
||||
return s1;
|
||||
};
|
||||
|
||||
const addSecret = (event: any) => {
|
||||
event.preventDefault();
|
||||
HttpService.makeCallToBackend({
|
||||
|
@ -26,16 +39,22 @@ export default function SecretNew() {
|
|||
});
|
||||
};
|
||||
|
||||
const warningStyle = {
|
||||
color: 'red',
|
||||
};
|
||||
|
||||
return (
|
||||
<main style={{ padding: '1rem 0' }}>
|
||||
<h2>Add Secret</h2>
|
||||
<Form onSubmit={addSecret}>
|
||||
<Form.Group className="mb-3" controlId="formDisplayName">
|
||||
<Form.Label>Key:</Form.Label>
|
||||
<Form.Label>
|
||||
Key: <span style={warningStyle}>No Spaces</span>
|
||||
</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
value={key}
|
||||
onChange={(e) => setKey(e.target.value)}
|
||||
onChange={(e) => setKey(changeSpacesToDash(e.target.value))}
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group className="mb-3" controlId="formIdentifier">
|
||||
|
@ -48,9 +67,14 @@ export default function SecretNew() {
|
|||
}}
|
||||
/>
|
||||
</Form.Group>
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
<Stack direction="horizontal" gap={3}>
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
<Button variant="danger" type="button" onClick={navigateToSecrets}>
|
||||
Cancel
|
||||
</Button>
|
||||
</Stack>
|
||||
</Form>
|
||||
</main>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { Stack, Table } from 'react-bootstrap';
|
||||
import { Stack, Table, Button } from 'react-bootstrap';
|
||||
import HttpService from '../services/HttpService';
|
||||
import { Secret } from '../interfaces';
|
||||
import ButtonWithConfirmation from '../components/ButtonWithConfirmation';
|
||||
|
@ -10,10 +10,7 @@ export default function SecretShow() {
|
|||
const params = useParams();
|
||||
|
||||
const [secret, setSecret] = useState<Secret | null>(null);
|
||||
|
||||
const navigateToSecrets = (_result: any) => {
|
||||
navigate(`/admin/secrets`);
|
||||
};
|
||||
const [secretValue, setSecretValue] = useState(secret?.value);
|
||||
|
||||
useEffect(() => {
|
||||
HttpService.makeCallToBackend({
|
||||
|
@ -22,6 +19,37 @@ export default function SecretShow() {
|
|||
});
|
||||
}, [params]);
|
||||
|
||||
const handleSecretValueChange = (event: any) => {
|
||||
if (secret) {
|
||||
setSecretValue(event.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
// const reloadSecret = (_result: any) => {
|
||||
// window.location.reload();
|
||||
// };
|
||||
|
||||
const updateSecretValue = () => {
|
||||
if (secret && secretValue) {
|
||||
secret.value = secretValue;
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/secrets/${secret.key}`,
|
||||
successCallback: () => {
|
||||
setSecret(secret);
|
||||
},
|
||||
httpMethod: 'PUT',
|
||||
postBody: {
|
||||
value: secretValue,
|
||||
creator_user_id: secret.creator_user_id,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const navigateToSecrets = (_result: any) => {
|
||||
navigate(`/admin/secrets`);
|
||||
};
|
||||
|
||||
const deleteSecret = () => {
|
||||
if (secret === null) {
|
||||
return;
|
||||
|
@ -34,17 +62,18 @@ export default function SecretShow() {
|
|||
};
|
||||
|
||||
if (secret) {
|
||||
const secretToUse = secret as any;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack direction="horizontal" gap={3}>
|
||||
<h2>Secret Key: {secretToUse.key}</h2>
|
||||
<h2>Secret Key: {secret.key}</h2>
|
||||
<ButtonWithConfirmation
|
||||
description="Delete Secret?"
|
||||
onConfirmation={deleteSecret}
|
||||
buttonLabel="Delete"
|
||||
/>
|
||||
<Button variant="warning" onClick={updateSecretValue}>
|
||||
Update Value
|
||||
</Button>
|
||||
</Stack>
|
||||
<div>
|
||||
<Table striped bordered>
|
||||
|
@ -57,7 +86,15 @@ export default function SecretShow() {
|
|||
<tbody>
|
||||
<tr>
|
||||
<td>{params.key}</td>
|
||||
<td>{secretToUse.value}</td>
|
||||
<td>
|
||||
<input
|
||||
id="secret_value"
|
||||
name="secret_value"
|
||||
type="text"
|
||||
value={secretValue || secret.value}
|
||||
onChange={handleSecretValueChange}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</Table>
|
||||
|
|
|
@ -17,7 +17,6 @@ export default function TaskShow() {
|
|||
const setErrorMessage = (useContext as any)(ErrorContext)[1];
|
||||
|
||||
useEffect(() => {
|
||||
setErrorMessage('');
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/tasks/${params.process_instance_id}/${params.task_id}`,
|
||||
successCallback: setTask,
|
||||
|
|
|
@ -67,11 +67,15 @@ backendCallProps) => {
|
|||
});
|
||||
|
||||
let isSuccessful = true;
|
||||
let is403 = false;
|
||||
fetch(`${BACKEND_BASE_URL}${path}`, httpArgs)
|
||||
.then((response) => {
|
||||
if (response.status === 401) {
|
||||
UserService.doLogin();
|
||||
throw new UnauthenticatedError('You must be authenticated to do this.');
|
||||
} else if (response.status === 403) {
|
||||
is403 = true;
|
||||
isSuccessful = false;
|
||||
} else if (!response.ok) {
|
||||
isSuccessful = false;
|
||||
}
|
||||
|
@ -80,6 +84,10 @@ backendCallProps) => {
|
|||
.then((result: any) => {
|
||||
if (isSuccessful) {
|
||||
successCallback(result);
|
||||
} else if (is403) {
|
||||
// Hopefully we can make this service a hook and use the error message context directly
|
||||
// eslint-disable-next-line no-alert
|
||||
alert(result.message);
|
||||
} else {
|
||||
let message = 'A server error occurred.';
|
||||
if (result.message) {
|
||||
|
@ -89,6 +97,8 @@ backendCallProps) => {
|
|||
failureCallback(message);
|
||||
} else {
|
||||
console.error(message);
|
||||
// eslint-disable-next-line no-alert
|
||||
alert(message);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue