mirror of
https://github.com/apache/superset.git
synced 2024-09-17 11:09:47 -04:00
fix: Tags Page Polish (#25403)
Co-authored-by: Lily Kuang <lily@preset.io>
This commit is contained in:
parent
0cebffd59a
commit
85251f8cae
@ -22,18 +22,33 @@ import { t, styled, logging } from '@superset-ui/core';
|
|||||||
import TableView, { EmptyWrapperType } from 'src/components/TableView';
|
import TableView, { EmptyWrapperType } from 'src/components/TableView';
|
||||||
import { addDangerToast } from 'src/components/MessageToasts/actions';
|
import { addDangerToast } from 'src/components/MessageToasts/actions';
|
||||||
import Loading from 'src/components/Loading';
|
import Loading from 'src/components/Loading';
|
||||||
|
import { TagsList } from 'src/components/Tags';
|
||||||
|
import FacePile from 'src/components/FacePile';
|
||||||
|
import Tag from 'src/types/TagType';
|
||||||
|
import Owner from 'src/types/Owner';
|
||||||
|
import { EmptyStateBig } from 'src/components/EmptyState';
|
||||||
import { fetchObjects } from '../tags/tags';
|
import { fetchObjects } from '../tags/tags';
|
||||||
|
|
||||||
|
const MAX_TAGS_TO_SHOW = 3;
|
||||||
|
|
||||||
const AllEntitiesTableContainer = styled.div`
|
const AllEntitiesTableContainer = styled.div`
|
||||||
text-align: left;
|
text-align: left;
|
||||||
border-radius: ${({ theme }) => theme.gridUnit * 1}px 0;
|
border-radius: ${({ theme }) => theme.gridUnit * 1}px 0;
|
||||||
margin: 0 ${({ theme }) => theme.gridUnit * 4}px;
|
|
||||||
.table {
|
.table {
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
}
|
}
|
||||||
.td {
|
.td {
|
||||||
width: 33%;
|
width: 33%;
|
||||||
}
|
}
|
||||||
|
.entity-title {
|
||||||
|
font-family: Inter;
|
||||||
|
font-size: ${({ theme }) => theme.typography.sizes.m}px;
|
||||||
|
font-weight: ${({ theme }) => theme.typography.weights.medium};
|
||||||
|
line-height: 17px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-align: left;
|
||||||
|
margin: ${({ theme }) => theme.gridUnit * 4}px 0;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface TaggedObject {
|
interface TaggedObject {
|
||||||
@ -44,6 +59,8 @@ interface TaggedObject {
|
|||||||
changed_on: moment.MomentInput;
|
changed_on: moment.MomentInput;
|
||||||
created_by: number | undefined;
|
created_by: number | undefined;
|
||||||
creator: string;
|
creator: string;
|
||||||
|
owners: Owner[];
|
||||||
|
tags: Tag[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TaggedObjects {
|
interface TaggedObjects {
|
||||||
@ -54,10 +71,12 @@ interface TaggedObjects {
|
|||||||
|
|
||||||
interface AllEntitiesTableProps {
|
interface AllEntitiesTableProps {
|
||||||
search?: string;
|
search?: string;
|
||||||
|
setShowTagModal: (show: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AllEntitiesTable({
|
export default function AllEntitiesTable({
|
||||||
search = '',
|
search = '',
|
||||||
|
setShowTagModal,
|
||||||
}: AllEntitiesTableProps) {
|
}: AllEntitiesTableProps) {
|
||||||
type objectType = 'dashboard' | 'chart' | 'query';
|
type objectType = 'dashboard' | 'chart' | 'query';
|
||||||
|
|
||||||
@ -66,8 +85,19 @@ export default function AllEntitiesTable({
|
|||||||
chart: [],
|
chart: [],
|
||||||
query: [],
|
query: [],
|
||||||
});
|
});
|
||||||
|
const [isLoading, setLoading] = useState<boolean>(true);
|
||||||
|
const showListViewObjs =
|
||||||
|
objects.dashboard.length > 0 ||
|
||||||
|
objects.chart.length > 0 ||
|
||||||
|
objects.query.length > 0;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (search === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
fetchObjects(
|
fetchObjects(
|
||||||
{ tags: search, types: null },
|
{ tags: search, types: null },
|
||||||
(data: TaggedObject[]) => {
|
(data: TaggedObject[]) => {
|
||||||
@ -77,6 +107,7 @@ export default function AllEntitiesTable({
|
|||||||
objects[object_type].push(object);
|
objects[object_type].push(object);
|
||||||
});
|
});
|
||||||
setObjects(objects);
|
setObjects(objects);
|
||||||
|
setLoading(false);
|
||||||
},
|
},
|
||||||
(error: Response) => {
|
(error: Response) => {
|
||||||
addDangerToast('Error Fetching Tagged Objects');
|
addDangerToast('Error Fetching Tagged Objects');
|
||||||
@ -89,7 +120,10 @@ export default function AllEntitiesTable({
|
|||||||
const data = objects[type].map((o: TaggedObject) => ({
|
const data = objects[type].map((o: TaggedObject) => ({
|
||||||
[type]: <a href={o.url}>{o.name}</a>,
|
[type]: <a href={o.url}>{o.name}</a>,
|
||||||
modified: moment.utc(o.changed_on).fromNow(),
|
modified: moment.utc(o.changed_on).fromNow(),
|
||||||
|
tags: o.tags,
|
||||||
|
owners: o.owners,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableView
|
<TableView
|
||||||
className="table-condensed"
|
className="table-condensed"
|
||||||
@ -99,27 +133,69 @@ export default function AllEntitiesTable({
|
|||||||
columns={[
|
columns={[
|
||||||
{
|
{
|
||||||
accessor: type,
|
accessor: type,
|
||||||
Header: type.charAt(0).toUpperCase() + type.slice(1),
|
Header: 'Title',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Cell: ({
|
||||||
|
row: {
|
||||||
|
original: { tags = [] },
|
||||||
|
},
|
||||||
|
}: {
|
||||||
|
row: {
|
||||||
|
original: {
|
||||||
|
tags: Tag[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}) => (
|
||||||
|
// Only show custom type tags
|
||||||
|
<TagsList
|
||||||
|
tags={tags.filter(
|
||||||
|
(tag: Tag) =>
|
||||||
|
tag.type === 'TagTypes.custom' || tag.type === 1,
|
||||||
|
)}
|
||||||
|
maxTags={MAX_TAGS_TO_SHOW}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
Header: t('Tags'),
|
||||||
|
accessor: 'tags',
|
||||||
|
disableSortBy: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Cell: ({
|
||||||
|
row: {
|
||||||
|
original: { owners = [] },
|
||||||
|
},
|
||||||
|
}: any) => <FacePile users={owners} />,
|
||||||
|
Header: t('Owners'),
|
||||||
|
accessor: 'owners',
|
||||||
|
disableSortBy: true,
|
||||||
|
size: 'xl',
|
||||||
},
|
},
|
||||||
{ accessor: 'modified', Header: 'Modified' },
|
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (objects) {
|
if (isLoading) return <Loading />;
|
||||||
return (
|
return (
|
||||||
<AllEntitiesTableContainer>
|
<AllEntitiesTableContainer>
|
||||||
<h3>{t('Dashboards')}</h3>
|
{showListViewObjs ? (
|
||||||
|
<>
|
||||||
|
<div className="entity-title">{t('Dashboards')}</div>
|
||||||
{renderTable('dashboard')}
|
{renderTable('dashboard')}
|
||||||
<hr />
|
<div className="entity-title">{t('Charts')}</div>
|
||||||
<h3>{t('Charts')}</h3>
|
|
||||||
{renderTable('chart')}
|
{renderTable('chart')}
|
||||||
<hr />
|
<div className="entity-title">{t('Queries')}</div>
|
||||||
<h3>{t('Queries')}</h3>
|
|
||||||
{renderTable('query')}
|
{renderTable('query')}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<EmptyStateBig
|
||||||
|
image="dashboard.svg"
|
||||||
|
title={t('No entities have this tag currently assigned')}
|
||||||
|
buttonAction={() => setShowTagModal(true)}
|
||||||
|
buttonText={t('Add tag to entities')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</AllEntitiesTableContainer>
|
</AllEntitiesTableContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return <Loading />;
|
|
||||||
}
|
|
||||||
|
@ -57,7 +57,7 @@ const AllEntitiesContainer = styled.div`
|
|||||||
margin-bottom: ${theme.gridUnit * 1}px;
|
margin-bottom: ${theme.gridUnit * 1}px;
|
||||||
}
|
}
|
||||||
.entities {
|
.entities {
|
||||||
margin: ${theme.gridUnit * 7.5}px; 0px;
|
margin: ${theme.gridUnit * 6}px; 0px;
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
@ -88,6 +88,7 @@ function AllEntities() {
|
|||||||
const [tag, setTag] = useState<Tag | null>(null);
|
const [tag, setTag] = useState<Tag | null>(null);
|
||||||
const [showTagModal, setShowTagModal] = useState<boolean>(false);
|
const [showTagModal, setShowTagModal] = useState<boolean>(false);
|
||||||
const { addSuccessToast, addDangerToast } = useToasts();
|
const { addSuccessToast, addDangerToast } = useToasts();
|
||||||
|
|
||||||
const editableTitleProps = {
|
const editableTitleProps = {
|
||||||
title: tag?.name || '',
|
title: tag?.name || '',
|
||||||
placeholder: 'testing',
|
placeholder: 'testing',
|
||||||
@ -166,10 +167,14 @@ function AllEntities() {
|
|||||||
menuDropdownProps={{
|
menuDropdownProps={{
|
||||||
disabled: true,
|
disabled: true,
|
||||||
}}
|
}}
|
||||||
|
showMenuDropdown={false}
|
||||||
/>
|
/>
|
||||||
</AllEntitiesNav>
|
</AllEntitiesNav>
|
||||||
<div className="entities">
|
<div className="entities">
|
||||||
<AllEntitiesTable search={tag?.name || ''} />
|
<AllEntitiesTable
|
||||||
|
search={tag?.name || ''}
|
||||||
|
setShowTagModal={setShowTagModal}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</AllEntitiesContainer>
|
</AllEntitiesContainer>
|
||||||
);
|
);
|
||||||
|
@ -86,7 +86,7 @@ interface DashboardListProps {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Dashboard {
|
export interface Dashboard {
|
||||||
changed_by_name: string;
|
changed_by_name: string;
|
||||||
changed_on_delta_humanized: string;
|
changed_on_delta_humanized: string;
|
||||||
changed_by: string;
|
changed_by: string;
|
||||||
|
@ -175,16 +175,6 @@ class TagDAO(BaseDAO[Tag]):
|
|||||||
returns a list of tagged objects filtered by tag names and object types
|
returns a list of tagged objects filtered by tag names and object types
|
||||||
if no filters applied returns all tagged objects
|
if no filters applied returns all tagged objects
|
||||||
"""
|
"""
|
||||||
# id = fields.Int()
|
|
||||||
# type = fields.String()
|
|
||||||
# name = fields.String()
|
|
||||||
# url = fields.String()
|
|
||||||
# changed_on = fields.DateTime()
|
|
||||||
# created_by = fields.Nested(UserSchema)
|
|
||||||
# creator = fields.String(
|
|
||||||
|
|
||||||
# filter types
|
|
||||||
|
|
||||||
results: list[dict[str, Any]] = []
|
results: list[dict[str, Any]] = []
|
||||||
|
|
||||||
# dashboards
|
# dashboards
|
||||||
@ -211,6 +201,8 @@ class TagDAO(BaseDAO[Tag]):
|
|||||||
"changed_on": obj.changed_on,
|
"changed_on": obj.changed_on,
|
||||||
"created_by": obj.created_by_fk,
|
"created_by": obj.created_by_fk,
|
||||||
"creator": obj.creator(),
|
"creator": obj.creator(),
|
||||||
|
"tags": obj.tags,
|
||||||
|
"owners": obj.owners,
|
||||||
}
|
}
|
||||||
for obj in dashboards
|
for obj in dashboards
|
||||||
)
|
)
|
||||||
@ -238,6 +230,8 @@ class TagDAO(BaseDAO[Tag]):
|
|||||||
"changed_on": obj.changed_on,
|
"changed_on": obj.changed_on,
|
||||||
"created_by": obj.created_by_fk,
|
"created_by": obj.created_by_fk,
|
||||||
"creator": obj.creator(),
|
"creator": obj.creator(),
|
||||||
|
"tags": obj.tags,
|
||||||
|
"owners": obj.owners,
|
||||||
}
|
}
|
||||||
for obj in charts
|
for obj in charts
|
||||||
)
|
)
|
||||||
@ -265,6 +259,8 @@ class TagDAO(BaseDAO[Tag]):
|
|||||||
"changed_on": obj.changed_on,
|
"changed_on": obj.changed_on,
|
||||||
"created_by": obj.created_by_fk,
|
"created_by": obj.created_by_fk,
|
||||||
"creator": obj.creator(),
|
"creator": obj.creator(),
|
||||||
|
"tags": obj.tags,
|
||||||
|
"owners": [obj.creator()],
|
||||||
}
|
}
|
||||||
for obj in saved_queries
|
for obj in saved_queries
|
||||||
)
|
)
|
||||||
|
@ -38,6 +38,12 @@ openapi_spec_methods_override = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TagGetResponseSchema(Schema):
|
||||||
|
id = fields.Int()
|
||||||
|
name = fields.String()
|
||||||
|
type = fields.String()
|
||||||
|
|
||||||
|
|
||||||
class TaggedObjectEntityResponseSchema(Schema):
|
class TaggedObjectEntityResponseSchema(Schema):
|
||||||
id = fields.Int()
|
id = fields.Int()
|
||||||
type = fields.String()
|
type = fields.String()
|
||||||
@ -46,12 +52,8 @@ class TaggedObjectEntityResponseSchema(Schema):
|
|||||||
changed_on = fields.DateTime()
|
changed_on = fields.DateTime()
|
||||||
created_by = fields.Nested(UserSchema(exclude=["username"]))
|
created_by = fields.Nested(UserSchema(exclude=["username"]))
|
||||||
creator = fields.String()
|
creator = fields.String()
|
||||||
|
tags = fields.List(fields.Nested(TagGetResponseSchema))
|
||||||
|
owners = fields.List(fields.Nested(UserSchema))
|
||||||
class TagGetResponseSchema(Schema):
|
|
||||||
id = fields.Int()
|
|
||||||
name = fields.String()
|
|
||||||
type = fields.String()
|
|
||||||
|
|
||||||
|
|
||||||
class TagObjectSchema(Schema):
|
class TagObjectSchema(Schema):
|
||||||
|
Loading…
Reference in New Issue
Block a user