Skip to content

Commit

Permalink
Implemented custom attribute management
Browse files Browse the repository at this point in the history
  • Loading branch information
rmathis committed May 25, 2021
1 parent 2507dc7 commit f1b1c23
Show file tree
Hide file tree
Showing 23 changed files with 1,576 additions and 171 deletions.
791 changes: 675 additions & 116 deletions ui/package-lock.json

Large diffs are not rendered by default.

102 changes: 102 additions & 0 deletions ui/public/assets/schema/attribute/attribute.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
{
"type": "object",
"required": [
"name",
"attributeType"
],
"properties": {
"name": {
"title": "label.entity-attribute-name",
"description": "tooltip.entity-attribute-name",
"type": "string",
"minLength": 1,
"maxLength": 255
},
"attributeType": {
"title": "label.entity-attribute-type",
"description": "tooltip.entity-attribute-type",
"type": "string",
"enum": [
"STRING",
"BOOLEAN",
"SELECTION_LIST"
],
"enumNames": [
"value.string",
"value.boolean",
"value.list"
]
},
"helpText": {
"title": "label.entity-attribute-help",
"description": "tooltip.entity-attribute-help",
"type": "string",
"minLength": 1,
"maxLength": 255
}
},
"dependencies": {
"attributeType": {
"oneOf": [
{
"properties": {
"attributeType": {
"enum": [
"STRING"
]
},
"defaultValueString": {
"title": "label.entity-attribute-default",
"description": "tooltip.entity-attribute-default",
"type": "string"
}
}
},
{
"properties": {
"attributeType": {
"enum": [
"BOOLEAN"
]
},
"defaultValueBoolean": {
"title": "label.entity-attribute-default",
"description": "tooltip.entity-attribute-default",
"type": "boolean",
"default": true,
"enumNames": ["True", "False"]
}
}
},
{
"properties": {
"attributeType": {
"enum": [
"SELECTION_LIST"
]
},
"customAttrListDefinitions": {
"title": "label.entity-attribute-list-options",
"description": "tooltip.entity-attribute-list-options",
"type": "array",
"items": {
"title": "label.list-options",
"type": "object",
"properties": {
"value": {
"type": "string",
"placeholder": "Option"
},
"default": {
"type": "boolean",
"default": false
}
}
}
}
}
}
]
}
}
}
2 changes: 2 additions & 0 deletions ui/src/app/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Dashboard from './dashboard/view/Dashboard';
import Header from './core/components/Header';
import { UserProvider } from './core/user/UserContext';
import { Metadata } from './metadata/Metadata';
import { Attribute } from './metadata/Attribute';
import { Notifications } from './notifications/hoc/Notifications';
import { NotificationList } from './notifications/component/NotificationList';
import { UserConfirmation, ConfirmWindow } from './core/components/UserConfirmation';
Expand Down Expand Up @@ -59,6 +60,7 @@ function App() {
<Route path="/dashboard" component={Dashboard} />
<Route path="/metadata/source/new" component={NewSource} />
<Route path="/metadata/provider/new" component={NewProvider} />
<Route path="/metadata/attributes" component={Attribute} />
<Route path={`/metadata/provider/:id/filter`} component={Filter} />
<Route path="/metadata/:type/:id" component={Metadata} />
</Switch>
Expand Down
16 changes: 14 additions & 2 deletions ui/src/app/core/components/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ export function Header () {
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="ml-auto align-items-center" navbar>
<Dropdown className="mr-2" id="basic-nav-dropdown">
<Dropdown.Toggle variant="outline-primary" id="dropdown-basic" size="sm">
<FontAwesomeIcon icon={faPlusCircle} className="mr-2" />
<Translate value={'action.advanced'} />
</Dropdown.Toggle>
<Dropdown.Menu>
<Link to="/metadata/attributes" className="dropdown-item text-primary py-2">
<FontAwesomeIcon icon={faCube} className="mr-2" />
<Translate value="action.custom-entity-attributes" />
</Link>
</Dropdown.Menu>
</Dropdown>
<Dropdown className="" id="basic-nav-dropdown">
<Dropdown.Toggle variant="outline-primary" id="dropdown-basic" size="sm">
<FontAwesomeIcon icon={faPlusCircle} className="mr-2" />
Expand All @@ -34,11 +46,11 @@ export function Header () {
<Dropdown.Menu>
<Link to="/metadata/source/new" className="dropdown-item text-primary py-2">
<FontAwesomeIcon icon={faCube} className="mr-2" />
Metadata Source
<Translate value="action.add-new-source" />
</Link>
<Link to="/metadata/provider/new" className="dropdown-item text-primary py-2">
<FontAwesomeIcon icon={faCubes} className="mr-2" />
Metadata Provider
<Translate value="action.add-new-provider" />
</Link>
</Dropdown.Menu>
</Dropdown>
Expand Down
147 changes: 147 additions & 0 deletions ui/src/app/form/component/fields/StringListWithDefaultField.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import React from 'react';

import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import Form from 'react-bootstrap/Form';

import Button from 'react-bootstrap/Button';

import AddButton from "../AddButton";

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTrash } from '@fortawesome/free-solid-svg-icons';

const ArrayFieldDescription = ({
DescriptionField,
idSchema,
description,
}) => {
if (!description) {
return null;
}

const id = `${idSchema.$id}__description`;
return <DescriptionField id={id} description={description} />;
};


const ArrayFieldTitle = ({
TitleField,
idSchema,
title,
required,
}) => {
if (!title) {
return null;
}

const id = `${idSchema.$id}__title`;
return <TitleField id={id} title={title} required={required} />;
};

const defItem = {
value: '',
default: false
}

const StringListWithDefaultField = ({
schema,
label,
id,
name,
disabled,
value,
readonly,
rawErrors,
onChange,
errorSchema,
formData,
registry,
...props
}) => {

const { fields } = registry;
const [items, setItems] = React.useState([
...(formData ? formData : [])
]);

React.useEffect(() => {
onChange(items);
}, [items, onChange]);

const onAdd = () => {
setItems([...items, { ...defItem }]);
};

const setValue = (item, value) => {
item.value = value;
setItems([...items]);
};

const setDefault = (item) => {
const current = items.find(i => i.default);
if (current) {
current.default = false;
}
item.default = true;
setItems([...items]);
};

const removeItem = (item) => {
setItems([...items.filter(i => i !== item)]);
};

return (
<div>
<Row className="">
<Col className="">
<div className="d-flex align-items-center mb-3">
{<ArrayFieldTitle
key={`array-field-title-${props.idSchema.$id}`}
TitleField={fields.TitleField}
idSchema={props.idSchema}
title={props.uiSchema["ui:title"] || props.title}
required={props.required}
/>}
<AddButton
className="array-item-add mx-2"
onClick={onAdd}
disabled={props.disabled || props.readonly}
/>
{(props.uiSchema["ui:description"] || schema.description) && (
<ArrayFieldDescription
key={`array-field-description-${props.idSchema.$id}`}
DescriptionField={fields.DescriptionField}
idSchema={props.idSchema}
description={
props.uiSchema["ui:description"] || schema.description
}
/>
)}
</div>
<div>
{items && items.map((p, idx) =>
<div className="my-2 d-flex justify-content-between align-items-center form-inline">
<Form.Control
type="text"
className="flex-grow-1"
value={p.value}
onChange={({ target: { value } }) => setValue(p, value)}></Form.Control>
<Form.Control custom name="default" type="radio" className="mx-4"
checked={ p.default }
onChange={ () => setDefault(p) }
></Form.Control>
<Button variant="text" className="text-danger" onClick={() => removeItem(p)}>
<FontAwesomeIcon icon={faTrash} />
</Button>
</div>
)}
</div>
</Col>
</Row>
</div>
);
};


export default StringListWithDefaultField;
4 changes: 3 additions & 1 deletion ui/src/app/form/component/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ import ObjectFieldTemplate from './templates/ObjectFieldTemplate';
import TitleField from './fields/TitleField';
import DescriptionField from './fields/DescriptionField';
import FilterTargetField from './fields/FilterTargetField';
import StringListWithDefaultField from './fields/StringListWithDefaultField';

export const fields = {
// SchemaField: CustomSchemaField
TitleField,
DescriptionField,
FilterTargetField
FilterTargetField,
StringListWithDefaultField
};

export const templates = {
Expand Down
Loading

0 comments on commit f1b1c23

Please sign in to comment.