From dda7d350be4b8acbef55f77cbb4baea378760569 Mon Sep 17 00:00:00 2001
From: zhouwenxuan <aries@Mac.local>
Date: Fri, 25 Apr 2025 10:37:31 +0800
Subject: [PATCH] add create tag btn on tags tree header

---
 .../dir-view-mode/dir-tags/index.js           | 49 +++++++++++++++++--
 .../src/components/tree-section/index.css     | 16 ++++--
 2 files changed, 56 insertions(+), 9 deletions(-)

diff --git a/frontend/src/components/dir-view-mode/dir-tags/index.js b/frontend/src/components/dir-view-mode/dir-tags/index.js
index a3ea26e1a8..6951353a03 100644
--- a/frontend/src/components/dir-view-mode/dir-tags/index.js
+++ b/frontend/src/components/dir-view-mode/dir-tags/index.js
@@ -1,12 +1,17 @@
-import React, { useMemo } from 'react';
+import React, { useCallback, useMemo, useState } from 'react';
 import PropTypes from 'prop-types';
 import { gettext } from '../../../utils/constants';
 import TreeSection from '../../tree-section';
 import { useMetadataStatus } from '../../../hooks';
 import { TagsTreeView } from '../../../tag';
 import { useTags } from '../../../tag/hooks';
+import EditTagDialog from '../../../tag/components/dialog/edit-tag-dialog';
 
 const DirTags = ({ userPerm, repoID, currentPath, currentRepoInfo }) => {
+  const [isShowEditTagDialog, setIsShowEditTagDialog] = useState(false);
+
+  const { enableMetadata, enableTags } = useMetadataStatus();
+  const { isLoading, tagsData, addTag } = useTags();
 
   const enableMetadataManagement = useMemo(() => {
     if (currentRepoInfo.encrypted) return false;
@@ -14,15 +19,51 @@ const DirTags = ({ userPerm, repoID, currentPath, currentRepoInfo }) => {
   // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [window.app.pageOptions.enableMetadataManagement, currentRepoInfo]);
 
-  const { enableMetadata, enableTags } = useMetadataStatus();
-  const { isLoading } = useTags();
+  const tags = useMemo(() => {
+    if (!tagsData) return [];
+    return tagsData.rows;
+  }, [tagsData]);
+
+  const createTag = useCallback((tag, callback) => {
+    addTag(tag, callback);
+  }, [addTag]);
+
+  const openAddTag = useCallback(() => {
+    setIsShowEditTagDialog(true);
+  }, []);
+
+  const closeAddTag = useCallback(() => {
+    setIsShowEditTagDialog(false);
+  }, []);
+
+  const renderTreeSectionHeaderOperations = (menuProps) => {
+    const canAdd = userPerm === 'rw' || userPerm === 'admin';
+
+    let operations = [];
+    if (enableTags && canAdd) {
+      operations.push(
+        <span key="tree-section-create-operation" role="button" className="tree-section-header-operation tree-section-create-operation" onClick={openAddTag}>
+          <i className="sf3-font sf3-font-new"></i>
+        </span>
+      );
+    }
+    return operations;
+  };
 
   if (!enableMetadataManagement) return null;
   if (!enableMetadata || !enableTags) return null;
 
   return (
-    <TreeSection repoID={repoID} title={gettext('Tags')} stateStorageKey="tags">
+    <TreeSection
+      repoID={repoID}
+      title={gettext('Tags')}
+      stateStorageKey="tags"
+      renderHeaderOperations={renderTreeSectionHeaderOperations}
+    >
       {!isLoading && (<TagsTreeView userPerm={userPerm} repoID={repoID} currentPath={currentPath} />)}
+      {isShowEditTagDialog && (
+        <EditTagDialog tags={tags} title={gettext('New tag')} onToggle={closeAddTag} onSubmit={createTag} />
+      )}
     </TreeSection>
   );
 };
diff --git a/frontend/src/components/tree-section/index.css b/frontend/src/components/tree-section/index.css
index f4c0f8bed4..da15b74eb4 100644
--- a/frontend/src/components/tree-section/index.css
+++ b/frontend/src/components/tree-section/index.css
@@ -57,7 +57,8 @@
   border-radius: 3px;
 }
 
-.tree-section .tree-section-header .sf3-font-down {
+.tree-section .tree-section-header .sf3-font-down,
+.tree-section .tree-section-header .sf3-font-new {
   color: #666666;
 }
 
@@ -69,23 +70,28 @@
   justify-content: center;
 }
 
-.tree-section .tree-section-header-operation .dropdown .sf-dropdown-toggle {
+.tree-section .tree-section-header-operation .dropdown .sf-dropdown-toggle,
+.tree-section .tree-section-header-operation.tree-section-create-operation {
   margin-left: 0;
   line-height: 1.5;
   font-size: 16px;
   color: #666 !important;
 }
 
-.tree-section .tree-section-more-operation .dropdown .sf-dropdown-toggle.sf3-font-new {
+.tree-section .tree-section-more-operation .dropdown .sf-dropdown-toggle.sf3-font-new,
+.tree-section .tree-section-create-operation .sf3-font-new {
   font-size: 12px;
 }
 
-.tree-section .tree-section-header .tree-section-more-operation {
+.tree-section .tree-section-header .tree-section-more-operation,
+.tree-section .tree-section-header .tree-section-create-operation {
   display: none;
 }
 
 .tree-section .tree-section-header.tree-section-header-hover .tree-section-more-operation,
-.tree-section .tree-section-header:not(.tree-section-header-hover):hover .tree-section-more-operation {
+.tree-section .tree-section-header:not(.tree-section-header-hover):hover .tree-section-more-operation,
+.tree-section .tree-section-header.tree-section-header-hover .tree-section-create-operation,
+.tree-section .tree-section-header:not(.tree-section-header-hover):hover .tree-section-create-operation  {
   display: flex;
 }