mirror of
https://github.com/apache/superset.git
synced 2026-06-05 15:49:27 +00:00
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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user