Skip to content

Commit

Permalink
Added filter editor
Browse files Browse the repository at this point in the history
  • Loading branch information
rmathis committed May 19, 2021
1 parent 9caead5 commit dbca74b
Show file tree
Hide file tree
Showing 10 changed files with 180 additions and 23 deletions.
11 changes: 6 additions & 5 deletions ui/src/app/form/component/fields/FilterTargetField.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { AsyncTypeahead } from 'react-bootstrap-typeahead';
import useFetch from 'use-http';
import queryString from 'query-string';
import API_BASE_PATH from '../../../App.constant';
import isNil from 'lodash/isNil';

const ToggleButton = ({ isOpen, onClick, disabled }) => (
<button
Expand All @@ -35,10 +36,10 @@ const FilterTargetField = ({
readonly,
rawErrors,
onChange,
errorSchema
errorSchema,
formData,
...props
}) => {
console.log(errorSchema)

const typeFieldName = `${name}Type`;

const type = schema.properties[typeFieldName];
Expand All @@ -47,8 +48,8 @@ const FilterTargetField = ({
value: type['enum'][e]
}));

const [selectedType, setSelectedType] = React.useState(value && value.hasOwnProperty(typeFieldName) ? typeOptions.find(t => value[typeFieldName] === t.value) : typeOptions[0]);
const [selectedTarget, setSelectedTarget] = React.useState([]);
const [selectedType, setSelectedType] = React.useState(formData && formData.hasOwnProperty(typeFieldName) ? typeOptions.find(t => formData[typeFieldName] === t.value) : typeOptions[0]);
const [selectedTarget, setSelectedTarget] = React.useState([...(formData.value && !isNil(formData.value) && !isNil(formData.value[0]) ? formData.value : [])]);

const [term, setSearchTerm] = React.useState('');
const [ids, setSearchIds] = React.useState([]);
Expand Down
4 changes: 4 additions & 0 deletions ui/src/app/metadata/Filter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { Switch, Route, useRouteMatch, Redirect } from 'react-router-dom';
import { NewFilter } from './new/NewFilter';
import { EditFilter } from './view/EditFilter';

export function Filter() {

Expand All @@ -11,6 +12,9 @@ export function Filter() {
<Route path={`${path}/new/:section`} render={() =>
<NewFilter />
} />
<Route path={`${path}/:filterId/edit/:section`} render={() =>
<EditFilter />
} />
<Redirect exact path={`${path}/new`} to={`${path}/new/common`} />
</Switch>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@ export const EntityAttributesFilterWizard = {
}
}
}, BaseFilterDefinition.uiSchema),
validator: (data = [], current = { id: null }) => {
validator: (data = [], current = { resourceId: null }) => {

const filters = current ? data.filter(s => s.id !== current.id) : data;
const filters = current ? data.filter(s => s.resourceId !== current.resourceId) : data;
const names = filters.map(s => s.name);

console.log(current)

return (formData, errors) => {
if (names.indexOf(formData.name) > -1) {
errors.name.addError('message.name-unique');
Expand Down
6 changes: 4 additions & 2 deletions ui/src/app/metadata/domain/filter/NameIdFilterDefinition.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ export const NameIDFilterWizard = {
type: 'NameIDFormat',
schema: `${API_BASE_PATH}/ui/NameIdFormatFilter`,
steps: [],
validator: (data = [], current = { id: null }) => {
validator: (data = [], current = { resourceId: null }) => {

const filters = current ? data.filter(s => s.id !== current.id) : data;


const filters = current ? data.filter(s => s.resourceId !== current.resourceId) : data;
const names = filters.map(s => s.entityId);

return (formData, errors) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export function MetadataFilterConfigurationListItem ({ filter, isLast, isFirst,
<div className="d-flex justify-content-end mb-2">
<div className="d-flex justify-content-between">
<Link className="btn btn-link"
to={`filter/${filter.resourceId}/edit`}>
to={`../filter/${filter.resourceId}/edit/common`}>
<FontAwesomeIcon icon={faEdit} className="sr-hidden" />&nbsp;
<Translate value="action.edit">Edit</Translate>
</Link>
Expand Down
36 changes: 25 additions & 11 deletions ui/src/app/metadata/editor/MetadataFilterEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { MetadataDefinitionContext, MetadataSchemaContext } from '../hoc/Metadat
import { MetadataEditorForm } from './MetadataEditorForm';
import { MetadataEditorNav } from './MetadataEditorNav';
import { useMetadataFilters } from '../hooks/api';
import { MetadataObjectContext } from '../hoc/MetadataSelector';
import { MetadataFilterContext } from '../hoc/MetadataFilterSelector';

export function MetadataFilterEditor({children}) {

Expand All @@ -21,7 +21,7 @@ export function MetadataFilterEditor({children}) {
const history = useHistory();
const definition = React.useContext(MetadataDefinitionContext);
const schema = React.useContext(MetadataSchemaContext);
const current = React.useContext(MetadataObjectContext);
const current = React.useContext(MetadataFilterContext);

const { state, dispatch } = React.useContext(MetadataFormContext);
const { metadata, errors } = state;
Expand All @@ -39,14 +39,28 @@ export function MetadataFilterEditor({children}) {
const validator = definition.validator(data, current);

return (
<React.Fragment>
<div className={`w-100 d-flex align-items-start ${errors.length > 0 ? 'justify-content-between' : 'justify-content-end'}`}>
{errors.length > 0 &&
<Alert variant="danger" className="align-self-start alert-compact mt-3 mt-lg-0">
<p className="m-0"><FontAwesomeIcon icon={faExclamationTriangle} size="lg" className="mr-2" /> <Translate value="message.editor-invalid" /></p>
</Alert>
}
{children(metadata, errors.length > 0)}
<div className="">
<div className="row">
<div className="col-6 d-lg-none order-1">
<MetadataEditorNav
onNavigate={onNavigate}
definition={definition}
current={section}
base={`/metadata/provider/${id}/edit`}
format='dropdown'
errors={errors}>
</MetadataEditorNav>
</div>
<div className="col-6 col-lg-3 order-2 text-right">
{children(metadata, errors.length > 0)}
</div>
<div className={`col-xs-12 col-lg-9 order-lg-1 order-3 align-items-start ${errors.length > 0 ? 'justify-content-between' : 'justify-content-end'}`}>
{errors.length > 0 &&
<Alert variant="danger" className="align-self-start alert-compact mt-3 mt-lg-0">
<p className="m-0"><FontAwesomeIcon icon={faExclamationTriangle} size="lg" className="mr-2" /> <Translate value="message.editor-invalid" /></p>
</Alert>
}
</div>
</div>
<hr />
<div className="row">
Expand All @@ -72,6 +86,6 @@ export function MetadataFilterEditor({children}) {
}
</div>
</div>
</React.Fragment>
</div>
);
}
42 changes: 42 additions & 0 deletions ui/src/app/metadata/hoc/MetadataFilterSelector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';
import { useLocation, useParams } from 'react-router';
import { useMetadataFilters } from '../hooks/api';

export const MetadataFilterContext = React.createContext();

/*eslint-disable react-hooks/exhaustive-deps*/
export function MetadataFilterSelector({ children }) {

let { id, filterId } = useParams();
const location = useLocation();

React.useEffect(() => {
if (location.state?.refresh) {
loadFilter(id);
}
}, [location, id])

const { get, response } = useMetadataFilters(id, {
cachePolicy: 'no-cache'
});

const [filter, setFilter] = React.useState([]);

async function loadFilter(filterId) {
const f = await get(`/${filterId}`);
if (response.ok) {
setFilter(f);
}
}
React.useEffect(() => { loadFilter(filterId) }, [id]);

return (
<MetadataFilterContext.Provider value={filter}>
{filter && filter.version &&
<React.Fragment>{children(filter)}</React.Fragment>
}
</MetadataFilterContext.Provider>
);
}

export default MetadataFilterSelector;
2 changes: 1 addition & 1 deletion ui/src/app/metadata/new/NewFilter.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function NewFilter() {
<MetadataForm>
<MetadataFilterEditor>
{(filter, isInvalid) =>
<div className="d-flex">
<div className="d-flex justify-content-end">
<button className="btn btn-info mr-2"
type="button"
onClick={() => save(filter)}
Expand Down
92 changes: 92 additions & 0 deletions ui/src/app/metadata/view/EditFilter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { faSave, faSpinner } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import React from 'react';
import { Prompt, useHistory, useParams } from 'react-router';
import Translate from '../../i18n/components/translate';
import { MetadataFilterEditor } from '../editor/MetadataFilterEditor';
import { MetadataForm } from '../hoc/MetadataFormContext';
import { MetadataSchema } from '../hoc/MetadataSchema';
import { useMetadataFilters } from '../hooks/api';
import { MetadataFilterSelector } from '../hoc/MetadataFilterSelector';

export function EditFilter() {

const { id, filterId } = useParams();
const history = useHistory();

const { put, response, loading } = useMetadataFilters(id, {});

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

async function save(metadata) {
await put(`${filterId}`, metadata);
if (response.ok) {
gotoDetail({ refresh: true });
}
};

const cancel = () => {
gotoDetail();
};

const gotoDetail = (state = null) => {
setBlocking(false);
history.push(`/metadata/provider/${id}`, state);
};

return (
<div className="container-fluid p-3">
<Prompt
when={blocking}
message={location =>
`message.unsaved-editor`
}
/>
<section className="section" tabIndex="0">
<div className="section-header bg-info px-4 py-2 text-white">
<span className="display-6"><Translate value="label.edit-filter">Edit filter</Translate></span>
</div>
<div className="section-body p-4 border border-top-0 border-info">
<MetadataFilterSelector>
{(filter) => {
return (
<MetadataSchema type={filter['@type']}>
<MetadataForm initial={filter}>
<React.Fragment>
<div className="container-fluid">
<div className="form-inline">
<label htmlFor="staticType" className="mr-3">Filter Type</label>
<input type="text" readOnly disabled className="form-control" id="staticType" value={filter['@type']} />
</div>
</div>
<hr />
<MetadataFilterEditor>
{(filter, isInvalid) =>
<div className="d-flex justify-content-end">
<button className="btn btn-info mr-2"
type="button"
onClick={() => save(filter)}
disabled={isInvalid || loading}
aria-label="Save changes to the metadata source. You will return to the dashboard">
<FontAwesomeIcon icon={loading ? faSpinner : faSave} pulse={loading} />&nbsp;
<Translate value="action.save">Save</Translate>
</button>
<button className="btn btn-secondary"
type="button"
onClick={() => cancel()} aria-label="Cancel changes, go back to dashboard">
<Translate value="action.cancel">Cancel</Translate>
</button>
</div>
}
</MetadataFilterEditor>
</React.Fragment>
</MetadataForm>
</MetadataSchema>
);
}}
</MetadataFilterSelector>
</div>
</section>
</div>
);
}
2 changes: 1 addition & 1 deletion ui/src/app/metadata/wizard/MetadataFilterTypeSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function MetadataFilterTypeSelector({ types = [], children, actions}) {
mode: 'onChange',
reValidateMode: 'onChange',
defaultValues: {
type: null
type: 'NameIDFormat'
},
resolver: undefined,
context: undefined,
Expand Down

0 comments on commit dbca74b

Please sign in to comment.