Skip to content

Commit

Permalink
Fixed group management
Browse files Browse the repository at this point in the history
  • Loading branch information
rmathis committed Jul 13, 2021
1 parent bbe62ab commit 47cd01d
Show file tree
Hide file tree
Showing 11 changed files with 138 additions and 24 deletions.
4 changes: 4 additions & 0 deletions backend/src/main/resources/i18n/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ action.add-new-group=Add new group
action.add-attribute=Add Attribute
action.custom-entity-attributes=Custom Entity Attributes
action.groups=Groups
action.source-group=Group

value.enabled=Enabled
value.disabled=Disabled
Expand Down Expand Up @@ -491,6 +492,9 @@ label.provider=Metadata Provider
message.delete-user-title=Delete User?
message.delete-user-body=You are requesting to delete a user. If you complete this process the user will be removed. This cannot be undone. Do you wish to continue?

message.delete-group-title=Delete Group?
message.delete-group-body=You are requesting to delete a group. If you complete this process the group will be removed. This cannot be undone. Do you wish to continue?

message.delete-attribute-title=Delete Attribute?
message.delete-attribute-body=You are requesting to delete a custom attribute. If you complete this process the attribute will be removed. This cannot be undone. Do you wish to continue?

Expand Down
5 changes: 3 additions & 2 deletions ui/src/app/admin/component/GroupForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Translate from '../../i18n/components/translate';
import { useGroupUiSchema } from '../hooks';
import { fields, widgets } from '../../form/component';
import { templates } from '../../form/component';
import { FormContext, setFormDataAction } from '../../form/FormManager';
import { FormContext, setFormDataAction, setFormErrorAction } from '../../form/FormManager';

function ErrorListTemplate() {
return (<></>);
Expand All @@ -17,8 +17,9 @@ function ErrorListTemplate() {
export function GroupForm ({group = {}, errors = [], loading = false, schema, onSave, onCancel}) {

const { dispatch } = React.useContext(FormContext);
const onChange = ({formData}) => {
const onChange = ({formData, errors}) => {
dispatch(setFormDataAction(formData));
dispatch(setFormErrorAction(errors));
};

const uiSchema = useGroupUiSchema();
Expand Down
14 changes: 13 additions & 1 deletion ui/src/app/admin/container/EditGroup.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,33 @@ import { FormManager } from '../../form/FormManager';

import { GroupForm } from '../component/GroupForm';
import { GroupProvider } from '../hoc/GroupProvider';
import { createNotificationAction, NotificationTypes, useNotificationDispatcher } from '../../notifications/hoc/Notifications';
import { useTranslator } from '../../i18n/hooks';

export function EditGroup() {

const { id } = useParams();

const notifier = useNotificationDispatcher();
const translator = useTranslator();

const history = useHistory();

const { put, response, loading } = useGroups();

const [blocking, setBlocking] = React.useState(false);

async function save(metadata) {
await put(``, metadata);
let toast;
const resp = await put(``, metadata);
if (response.ok) {
gotoDetail({ refresh: true });
toast = createNotificationAction(`Updated group successfully.`, NotificationTypes.SUCCESS);
} else {
toast = createNotificationAction(`${resp.errorCode} - ${translator(resp.errorMessage)}`, NotificationTypes.ERROR);
}
if (toast) {
notifier(toast);
}
};

Expand Down
10 changes: 6 additions & 4 deletions ui/src/app/admin/container/GroupsList.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function GroupsList({ groups, onDelete }) {
}

return (
<DeleteConfirmation title={`message.delete-attribute-title`} body={`message.delete-attribute-body`}>
<DeleteConfirmation title={`message.delete-group-title`} body={`message.delete-group-body`}>
{(block) =>
<div className="container-fluid p-3">
<section className="section">
Expand Down Expand Up @@ -47,10 +47,10 @@ export function GroupsList({ groups, onDelete }) {
</tr>
</thead>
<tbody>
{(groups?.length > 0 ) ? groups.map((group, i) =>
{( groups?.length > 0 ) ? groups.map((group, i) =>
<tr key={i}>
<td>{group.name}</td>
<td>{group.description}</td>
<td>{group.description || ''}</td>
<td className="text-right">
<Link to={`../groups/${group.resourceId}/edit`} className="btn btn-link text-primary">
<FontAwesomeIcon icon={faEdit} size="lg" />
Expand All @@ -66,7 +66,9 @@ export function GroupsList({ groups, onDelete }) {
</Button>
</td>
</tr>
) : ''}
) : <tr>
<td colSpan="3">No groups defined.</td>
</tr>}
</tbody>
</table>
</div>
Expand Down
14 changes: 13 additions & 1 deletion ui/src/app/admin/container/NewGroup.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,29 @@ import { Schema } from '../../form/Schema';
import { FormManager } from '../../form/FormManager';
import { GroupForm } from '../component/GroupForm';

import { createNotificationAction, NotificationTypes, useNotificationDispatcher } from '../../notifications/hoc/Notifications';
import { useTranslator } from '../../i18n/hooks';

export function NewGroup() {
const history = useHistory();
const notifier = useNotificationDispatcher();
const translator = useTranslator();

const { post, response, loading } = useGroups({});

const [blocking, setBlocking] = React.useState(false);

async function save(group) {
await post(``, group);
let toast;
const resp = await post(``, group);
if (response.ok) {
gotoDetail({ refresh: true });
toast = createNotificationAction(`Added group successfully.`, NotificationTypes.SUCCESS);
} else {
toast = createNotificationAction(`${resp.errorCode} - ${translator(resp.errorMessage)}`, NotificationTypes.ERROR);
}
if (toast) {
notifier(toast);
}
};

Expand Down
13 changes: 12 additions & 1 deletion ui/src/app/admin/hoc/GroupsProvider.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import React from 'react';
import { useGroups } from '../hooks';
import { createNotificationAction, NotificationTypes, useNotificationDispatcher } from '../../notifications/hoc/Notifications';
import { useTranslator } from '../../i18n/hooks';

export function GroupsProvider({ children, cache = 'no-cache' }) {

const [groups, setGroups] = React.useState([]);

const notifier = useNotificationDispatcher();
const translator = useTranslator();

const { get, del, response, loading } = useGroups({
cachePolicy: cache
Expand All @@ -18,9 +22,16 @@ export function GroupsProvider({ children, cache = 'no-cache' }) {
}

async function removeGroup(id) {
await del(`/${id}`);
let toast;
const resp = await del(`/${id}`);
if (response.ok) {
loadGroups();
toast = createNotificationAction(`Deleted group successfully.`, NotificationTypes.SUCCESS);
} else {
toast = createNotificationAction(`${resp.errorCode} - ${translator(resp.errorMessage)}`, NotificationTypes.ERROR);
}
if (toast) {
notifier(toast);
}
}

Expand Down
2 changes: 1 addition & 1 deletion ui/src/app/dashboard/view/SourcesTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function SourcesTab () {
React.useEffect(() => { loadSources() }, []);

async function changeSourceGroup(source, group) {
const sources = await updater.put(`/${source.id}`, {
await updater.put(`/${source.id}`, {
...source,
groupId: group
});
Expand Down
2 changes: 0 additions & 2 deletions ui/src/app/form/FormManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ function FormManager({ children, initial = {} }) {
...initialState,
data
});


const contextValue = React.useMemo(() => ({ state, dispatch }), [state, dispatch]);

return (
Expand Down
78 changes: 70 additions & 8 deletions ui/src/app/metadata/component/MetadataHeader.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,82 @@
import React from 'react';
import FormattedDate from '../../core/components/FormattedDate';
import { useIsAdmin } from '../../core/user/UserContext';

import Translate from '../../i18n/components/translate';
import { GroupsProvider } from '../../admin/hoc/GroupsProvider';
import { useMetadataEntity } from '../hooks/api';
import { createNotificationAction, NotificationTypes, useNotificationDispatcher } from '../../notifications/hoc/Notifications';
import { useTranslator } from '../../i18n/hooks';
import { useMetadataLoader } from '../hoc/MetadataSelector';

export function MetadataHeader ({ showGroup, model, current = true, enabled = true, children, ...props }) {

const isAdmin = useIsAdmin();
const translator = useTranslator();

const { put, response } = useMetadataEntity('source', {
cachePolicy: 'no-cache'
});

const notifier = useNotificationDispatcher();

async function changeSourceGroup(s, group) {
let toast;
const resp = await put(`/${s.id}`, {
...s,
groupId: group
});
if (response.ok) {
toast = createNotificationAction(`Updated group successfully.`, NotificationTypes.SUCCESS);
reload();
} else {
toast = createNotificationAction(`${resp.errorCode} - ${translator(resp.errorMessage)}`, NotificationTypes.ERROR);
}
if (toast) {
notifier(toast);
}
}

const reload = useMetadataLoader();

export function MetadataHeader ({ model, current = true, enabled = true, children, ...props }) {
return (
<div className="card enabled-status" {...props}>
<div className="card-body">
<div className="d-flex justify-content-between">
<h5 className="card-title version-title">
<Translate value="label.saved">Saved</Translate>:&nbsp;
<span className="save-date">
<FormattedDate date={model.modifiedDate} time={true} />
</span>
<br />
<Translate value="label.by">By</Translate>:&nbsp;
<span className="author">{model.createdBy }</span>
<p className="mb-1">
<Translate value="label.saved">Saved</Translate>:&nbsp;
<span className="save-date mb-2">
<FormattedDate date={model.modifiedDate} time={true} />
</span>
</p>
<p className="mb-1">
<Translate value="label.by">By</Translate>:&nbsp;
<span className="author">{model.createdBy }</span>
</p>
{isAdmin && showGroup &&
<GroupsProvider>
{(groups, removeGroup, loadingGroups) =>
<div className="form-inline">
<label className="mr-2" htmlFor={`group-${model.serviceProviderName}`}><Translate value="action.source-group">Group</Translate>: </label>
<select
id={`group-${model.id}`}
name={`group-${model.id}`}
className="form-control form-control-sm"
onChange={(event) => changeSourceGroup(model, event.target.value)}
value={model.groupId}
disabled={loadingGroups}
disablevalidation="true">
<option>Select Group</option>
{groups.map((g, ridx) => (
<option key={ridx} value={g.resourceId}>{g.name}</option>
))}
</select>

</div>
}
</GroupsProvider>
}
</h5>
{children}
</div>
Expand All @@ -29,6 +90,7 @@ export function MetadataHeader ({ model, current = true, enabled = true, childre
<Translate value={`value.${current ? 'current' : 'not-current'}`}>Current</Translate>
</span>
</p>

</div>
</div>
);
Expand Down
16 changes: 13 additions & 3 deletions ui/src/app/metadata/hoc/MetadataSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useMetadataEntity } from '../hooks/api';

export const MetadataTypeContext = React.createContext();
export const MetadataObjectContext = React.createContext();
export const MetadataLoaderContext = React.createContext();

/*eslint-disable react-hooks/exhaustive-deps*/
export function MetadataSelector({ children, ...props }) {
Expand Down Expand Up @@ -31,23 +32,32 @@ export function MetadataSelector({ children, ...props }) {
setMetadata(source);
}
}
React.useEffect(() => { loadMetadata(id) }, [id]);

function reload() {
loadMetadata(id);
}

React.useEffect(() => reload(), [id]);

return (
<>
<MetadataLoaderContext.Provider value={ reload }>
{type &&
<MetadataTypeContext.Provider value={type}>
{metadata && metadata.version &&
<MetadataObjectContext.Provider value={metadata}>{children(metadata)}</MetadataObjectContext.Provider>
}
</MetadataTypeContext.Provider>
}
</>
</MetadataLoaderContext.Provider>
);
}

export function useMetadataObject () {
return React.useContext(MetadataObjectContext);
}

export function useMetadataLoader() {
return React.useContext(MetadataLoaderContext);
}

export default MetadataSelector;
4 changes: 3 additions & 1 deletion ui/src/app/metadata/view/MetadataOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { MetadataFilterTypes } from '../domain/filter';
import { useMetadataSchema } from '../hooks/schema';
import { FilterableProviders } from '../domain/provider';


export function MetadataOptions () {

const metadata = React.useContext(MetadataObjectContext);
Expand Down Expand Up @@ -60,7 +61,8 @@ export function MetadataOptions () {
<MetadataHeader
current={true}
enabled={type === 'source' ? metadata.serviceEnabled : metadata.enabled}
model={metadata}>
model={metadata}
showGroup={type === 'source'}>
{type === 'source' && onDeleteSource &&
<Button className="btn btn-outline btn-sm btn-danger align-self-start"
disabled={metadata.serviceEnabled}
Expand Down

0 comments on commit 47cd01d

Please sign in to comment.