This is an automated email from the ASF dual-hosted git repository.

hugh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 85251f8cae fix: Tags Page Polish (#25403)
85251f8cae is described below

commit 85251f8cae6fa2d6a77a73697fb0575cd37c1c92
Author: Hugh A. Miles II <[email protected]>
AuthorDate: Fri Sep 29 22:13:26 2023 -0400

    fix: Tags Page Polish (#25403)
    
    Co-authored-by: Lily Kuang <[email protected]>
---
 .../src/features/allEntities/AllEntitiesTable.tsx  | 112 +++++++++++++++++----
 superset-frontend/src/pages/AllEntities/index.tsx  |   9 +-
 .../src/pages/DashboardList/index.tsx              |   2 +-
 superset/daos/tag.py                               |  16 ++-
 superset/tags/schemas.py                           |  14 +--
 5 files changed, 116 insertions(+), 37 deletions(-)

diff --git a/superset-frontend/src/features/allEntities/AllEntitiesTable.tsx 
b/superset-frontend/src/features/allEntities/AllEntitiesTable.tsx
index 79e1c11ecc..75491c5baa 100644
--- a/superset-frontend/src/features/allEntities/AllEntitiesTable.tsx
+++ b/superset-frontend/src/features/allEntities/AllEntitiesTable.tsx
@@ -22,18 +22,33 @@ import { t, styled, logging } from '@superset-ui/core';
 import TableView, { EmptyWrapperType } from 'src/components/TableView';
 import { addDangerToast } from 'src/components/MessageToasts/actions';
 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';
 
+const MAX_TAGS_TO_SHOW = 3;
+
 const AllEntitiesTableContainer = styled.div`
   text-align: left;
   border-radius: ${({ theme }) => theme.gridUnit * 1}px 0;
-  margin: 0 ${({ theme }) => theme.gridUnit * 4}px;
   .table {
     table-layout: fixed;
   }
   .td {
     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 {
@@ -44,6 +59,8 @@ interface TaggedObject {
   changed_on: moment.MomentInput;
   created_by: number | undefined;
   creator: string;
+  owners: Owner[];
+  tags: Tag[];
 }
 
 interface TaggedObjects {
@@ -54,10 +71,12 @@ interface TaggedObjects {
 
 interface AllEntitiesTableProps {
   search?: string;
+  setShowTagModal: (show: boolean) => void;
 }
 
 export default function AllEntitiesTable({
   search = '',
+  setShowTagModal,
 }: AllEntitiesTableProps) {
   type objectType = 'dashboard' | 'chart' | 'query';
 
@@ -66,8 +85,19 @@ export default function AllEntitiesTable({
     chart: [],
     query: [],
   });
+  const [isLoading, setLoading] = useState<boolean>(true);
+  const showListViewObjs =
+    objects.dashboard.length > 0 ||
+    objects.chart.length > 0 ||
+    objects.query.length > 0;
 
   useEffect(() => {
+    if (search === '') {
+      return;
+    }
+
+    setLoading(true);
+
     fetchObjects(
       { tags: search, types: null },
       (data: TaggedObject[]) => {
@@ -77,6 +107,7 @@ export default function AllEntitiesTable({
           objects[object_type].push(object);
         });
         setObjects(objects);
+        setLoading(false);
       },
       (error: Response) => {
         addDangerToast('Error Fetching Tagged Objects');
@@ -89,7 +120,10 @@ export default function AllEntitiesTable({
     const data = objects[type].map((o: TaggedObject) => ({
       [type]: <a href={o.url}>{o.name}</a>,
       modified: moment.utc(o.changed_on).fromNow(),
+      tags: o.tags,
+      owners: o.owners,
     }));
+
     return (
       <TableView
         className="table-condensed"
@@ -99,27 +133,69 @@ export default function AllEntitiesTable({
         columns={[
           {
             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) {
-    return (
-      <AllEntitiesTableContainer>
-        <h3>{t('Dashboards')}</h3>
-        {renderTable('dashboard')}
-        <hr />
-        <h3>{t('Charts')}</h3>
-        {renderTable('chart')}
-        <hr />
-        <h3>{t('Queries')}</h3>
-        {renderTable('query')}
-      </AllEntitiesTableContainer>
-    );
-  }
-  return <Loading />;
+  if (isLoading) return <Loading />;
+  return (
+    <AllEntitiesTableContainer>
+      {showListViewObjs ? (
+        <>
+          <div className="entity-title">{t('Dashboards')}</div>
+          {renderTable('dashboard')}
+          <div className="entity-title">{t('Charts')}</div>
+          {renderTable('chart')}
+          <div className="entity-title">{t('Queries')}</div>
+          {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>
+  );
 }
diff --git a/superset-frontend/src/pages/AllEntities/index.tsx 
b/superset-frontend/src/pages/AllEntities/index.tsx
index eaa8e35703..63d5883537 100644
--- a/superset-frontend/src/pages/AllEntities/index.tsx
+++ b/superset-frontend/src/pages/AllEntities/index.tsx
@@ -57,7 +57,7 @@ const AllEntitiesContainer = styled.div`
     margin-bottom: ${theme.gridUnit * 1}px;
   }
   .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 [showTagModal, setShowTagModal] = useState<boolean>(false);
   const { addSuccessToast, addDangerToast } = useToasts();
+
   const editableTitleProps = {
     title: tag?.name || '',
     placeholder: 'testing',
@@ -166,10 +167,14 @@ function AllEntities() {
           menuDropdownProps={{
             disabled: true,
           }}
+          showMenuDropdown={false}
         />
       </AllEntitiesNav>
       <div className="entities">
-        <AllEntitiesTable search={tag?.name || ''} />
+        <AllEntitiesTable
+          search={tag?.name || ''}
+          setShowTagModal={setShowTagModal}
+        />
       </div>
     </AllEntitiesContainer>
   );
diff --git a/superset-frontend/src/pages/DashboardList/index.tsx 
b/superset-frontend/src/pages/DashboardList/index.tsx
index 29bb51b961..d7760ffbbc 100644
--- a/superset-frontend/src/pages/DashboardList/index.tsx
+++ b/superset-frontend/src/pages/DashboardList/index.tsx
@@ -86,7 +86,7 @@ interface DashboardListProps {
   };
 }
 
-interface Dashboard {
+export interface Dashboard {
   changed_by_name: string;
   changed_on_delta_humanized: string;
   changed_by: string;
diff --git a/superset/daos/tag.py b/superset/daos/tag.py
index c063657ea0..b6872a5376 100644
--- a/superset/daos/tag.py
+++ b/superset/daos/tag.py
@@ -175,16 +175,6 @@ class TagDAO(BaseDAO[Tag]):
         returns a list of tagged objects filtered by tag names and object types
         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]] = []
 
         # dashboards
@@ -211,6 +201,8 @@ class TagDAO(BaseDAO[Tag]):
                     "changed_on": obj.changed_on,
                     "created_by": obj.created_by_fk,
                     "creator": obj.creator(),
+                    "tags": obj.tags,
+                    "owners": obj.owners,
                 }
                 for obj in dashboards
             )
@@ -238,6 +230,8 @@ class TagDAO(BaseDAO[Tag]):
                     "changed_on": obj.changed_on,
                     "created_by": obj.created_by_fk,
                     "creator": obj.creator(),
+                    "tags": obj.tags,
+                    "owners": obj.owners,
                 }
                 for obj in charts
             )
@@ -265,6 +259,8 @@ class TagDAO(BaseDAO[Tag]):
                     "changed_on": obj.changed_on,
                     "created_by": obj.created_by_fk,
                     "creator": obj.creator(),
+                    "tags": obj.tags,
+                    "owners": [obj.creator()],
                 }
                 for obj in saved_queries
             )
diff --git a/superset/tags/schemas.py b/superset/tags/schemas.py
index 571a2a03c9..75fdc2410a 100644
--- a/superset/tags/schemas.py
+++ b/superset/tags/schemas.py
@@ -38,6 +38,12 @@ openapi_spec_methods_override = {
 }
 
 
+class TagGetResponseSchema(Schema):
+    id = fields.Int()
+    name = fields.String()
+    type = fields.String()
+
+
 class TaggedObjectEntityResponseSchema(Schema):
     id = fields.Int()
     type = fields.String()
@@ -46,12 +52,8 @@ class TaggedObjectEntityResponseSchema(Schema):
     changed_on = fields.DateTime()
     created_by = fields.Nested(UserSchema(exclude=["username"]))
     creator = fields.String()
-
-
-class TagGetResponseSchema(Schema):
-    id = fields.Int()
-    name = fields.String()
-    type = fields.String()
+    tags = fields.List(fields.Nested(TagGetResponseSchema))
+    owners = fields.List(fields.Nested(UserSchema))
 
 
 class TagObjectSchema(Schema):

Reply via email to