feat: Frontend tagging (#20876)

Co-authored-by: cccs-nik <68961854+cccs-nik@users.noreply.github.com>
Co-authored-by: GITHUB_USERNAME <EMAIL>
This commit is contained in:
cccs-RyanK
2023-02-21 16:38:23 -05:00
committed by GitHub
parent eb8386e3f0
commit a40c12d63e
58 changed files with 4008 additions and 286 deletions

View File

@@ -23,10 +23,19 @@ import Button from 'src/components/Button';
import { AsyncSelect, Row, Col, AntdForm } from 'src/components';
import { SelectValue } from 'antd/lib/select';
import rison from 'rison';
import { t, SupersetClient, styled } from '@superset-ui/core';
import {
t,
SupersetClient,
styled,
isFeatureEnabled,
FeatureFlag,
} from '@superset-ui/core';
import Chart, { Slice } from 'src/types/Chart';
import { getClientErrorObject } from 'src/utils/getClientErrorObject';
import withToasts from 'src/components/MessageToasts/withToasts';
import { loadTags } from 'src/components/Tags/utils';
import { addTag, deleteTaggedObjects, fetchTags, OBJECT_TYPES } from 'src/tags';
import TagType from 'src/types/TagType';
export type PropertiesModalProps = {
slice: Slice;
@@ -63,6 +72,17 @@ function PropertiesModal({
null,
);
const [tags, setTags] = useState<TagType[]>([]);
const tagsAsSelectValues = useMemo(() => {
const selectTags = tags.map(tag => ({
value: tag.name,
label: tag.name,
key: tag.name,
}));
return selectTags;
}, [tags.length]);
function showError({ error, statusText, message }: any) {
let errorText = error || statusText || t('An error has occurred');
if (message === 'Forbidden') {
@@ -119,6 +139,41 @@ function PropertiesModal({
[],
);
const updateTags = (oldTags: TagType[], newTags: TagType[]) => {
// update the tags for this object
// add tags that are in new tags, but not in old tags
// eslint-disable-next-line array-callback-return
newTags.map((tag: TagType) => {
if (!oldTags.some(t => t.name === tag.name)) {
addTag(
{
objectType: OBJECT_TYPES.CHART,
objectId: slice.slice_id,
includeTypes: false,
},
tag.name,
() => {},
() => {},
);
}
});
// delete tags that are in old tags, but not in new tags
// eslint-disable-next-line array-callback-return
oldTags.map((tag: TagType) => {
if (!newTags.some(t => t.name === tag.name)) {
deleteTaggedObjects(
{
objectType: OBJECT_TYPES.CHART,
objectId: slice.slice_id,
},
tag,
() => {},
() => {},
);
}
});
};
const onSubmit = async (values: {
certified_by?: string;
certification_details?: string;
@@ -148,6 +203,25 @@ function PropertiesModal({
}[]
).map(o => o.value);
}
if (isFeatureEnabled(FeatureFlag.TAGGING_SYSTEM)) {
// update tags
try {
fetchTags(
{
objectType: OBJECT_TYPES.CHART,
objectId: slice.slice_id,
includeTypes: false,
},
(currentTags: TagType[]) => updateTags(currentTags, tags),
error => {
showError(error);
},
);
} catch (error) {
showError(error);
}
}
try {
const res = await SupersetClient.put({
endpoint: `/api/v1/chart/${slice.slice_id}`,
@@ -158,6 +232,7 @@ function PropertiesModal({
const updatedChart = {
...payload,
...res.json.result,
tags,
id: slice.slice_id,
owners: selectedOwners,
};
@@ -183,6 +258,37 @@ function PropertiesModal({
setName(slice.slice_name || '');
}, [slice.slice_name]);
useEffect(() => {
if (!isFeatureEnabled(FeatureFlag.TAGGING_SYSTEM)) return;
try {
fetchTags(
{
objectType: OBJECT_TYPES.CHART,
objectId: slice.slice_id,
includeTypes: false,
},
(tags: TagType[]) => setTags(tags),
error => {
showError(error);
},
);
} catch (error) {
showError(error);
}
}, [slice.slice_id]);
const handleChangeTags = (values: { label: string; value: number }[]) => {
// triggered whenever a new tag is selected or a tag was deselected
// on new tag selected, add the tag
const uniqueTags = [...new Set(values.map(v => v.label))];
setTags([...uniqueTags.map(t => ({ name: t }))]);
};
const handleClearTags = () => {
setTags([]);
};
return (
<Modal
show={show}
@@ -315,6 +421,26 @@ function PropertiesModal({
)}
</StyledHelpBlock>
</FormItem>
{isFeatureEnabled(FeatureFlag.TAGGING_SYSTEM) && (
<h3 css={{ marginTop: '1em' }}>{t('Tags')}</h3>
)}
{isFeatureEnabled(FeatureFlag.TAGGING_SYSTEM) && (
<FormItem>
<AsyncSelect
ariaLabel="Tags"
mode="multiple"
allowNewOptions
value={tagsAsSelectValues}
options={loadTags}
onChange={handleChangeTags}
onClear={handleClearTags}
allowClear
/>
<StyledHelpBlock className="help-block">
{t('A list of tags that have been applied to this chart.')}
</StyledHelpBlock>
</FormItem>
)}
</Col>
</Row>
</AntdForm>