From 9608599dd2f1949f7220ad3b571052af66f31bd0 Mon Sep 17 00:00:00 2001
From: Sean Hatfield <seanhatfield5@gmail.com>
Date: Tue, 26 Nov 2024 11:39:37 -0800
Subject: [PATCH] Community hub integration UX improvements (#2727)

* add empty states to your account hub page + disconnect button for api key

* lint

* lint

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>
---
 .../SlashCommands/SlashPresets/index.jsx      |  2 +-
 .../Authentication/UserItems/index.jsx        | 16 +++++-
 .../CommunityHub/Authentication/index.jsx     | 52 +++++++++++++++----
 .../PullAndReview/HubItem/AgentSkill.jsx      | 12 +++--
 4 files changed, 66 insertions(+), 16 deletions(-)

diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/SlashPresets/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/SlashPresets/index.jsx
index ec42050a2..e7e5536ea 100644
--- a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/SlashPresets/index.jsx
+++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/SlashPresets/index.jsx
@@ -100,7 +100,7 @@ export default function SlashPresets({ setShowing, sendCommand }) {
               e.stopPropagation();
               handleEditPreset(preset);
             }}
-            className="text-theme-text-primary text-sm p-1 hover:cursor-pointer hover:bg-theme-action-menu-item-hover rounded-full mt-1"
+            className="border-none text-theme-text-primary text-sm p-1 hover:cursor-pointer hover:bg-theme-action-menu-item-hover rounded-full mt-1"
           >
             <DotsThree size={24} weight="bold" />
           </button>
diff --git a/frontend/src/pages/GeneralSettings/CommunityHub/Authentication/UserItems/index.jsx b/frontend/src/pages/GeneralSettings/CommunityHub/Authentication/UserItems/index.jsx
index 03787b02a..89457542e 100644
--- a/frontend/src/pages/GeneralSettings/CommunityHub/Authentication/UserItems/index.jsx
+++ b/frontend/src/pages/GeneralSettings/CommunityHub/Authentication/UserItems/index.jsx
@@ -9,6 +9,10 @@ export default function UserItems({ connectionKey }) {
   const { createdByMe = {}, teamItems = [] } = userItems || {};
 
   if (loading) return <HubItemCardSkeleton />;
+  const hasItems = (items) => {
+    return Object.values(items).some((category) => category?.items?.length > 0);
+  };
+
   return (
     <div className="flex flex-col gap-y-8">
       {/* Created By Me Section */}
@@ -46,6 +50,11 @@ export default function UserItems({ connectionKey }) {
               </div>
             );
           })}
+          {!hasItems(createdByMe) && (
+            <p className="text-white/60 text-xs text-center mt-4">
+              You haven&apos;t created any items yet.
+            </p>
+          )}
         </div>
       </div>
 
@@ -66,7 +75,7 @@ export default function UserItems({ connectionKey }) {
                 {team.teamName}
               </h3>
               {Object.keys(team.items).map((type) => {
-                if (team.items[type].items.length === 0) return null;
+                if (!team.items[type]?.items?.length) return null;
                 return (
                   <div key={type} className="rounded-lg w-full">
                     <h3 className="text-white capitalize font-medium mb-3">
@@ -80,6 +89,11 @@ export default function UserItems({ connectionKey }) {
                   </div>
                 );
               })}
+              {!hasItems(team.items) && (
+                <p className="text-white/60 text-xs text-center mt-4">
+                  No items shared with this team yet.
+                </p>
+              )}
             </div>
           ))}
         </div>
diff --git a/frontend/src/pages/GeneralSettings/CommunityHub/Authentication/index.jsx b/frontend/src/pages/GeneralSettings/CommunityHub/Authentication/index.jsx
index 98f4450c3..7cf0d1c76 100644
--- a/frontend/src/pages/GeneralSettings/CommunityHub/Authentication/index.jsx
+++ b/frontend/src/pages/GeneralSettings/CommunityHub/Authentication/index.jsx
@@ -46,6 +46,26 @@ function useCommunityHubAuthentication() {
     }
   }
 
+  async function disconnectHub() {
+    setLoading(true);
+    try {
+      const response = await CommunityHub.updateSettings({
+        hub_api_key: "",
+      });
+      if (!response.success)
+        return showToast("Failed to disconnect from hub", "error");
+      setHasChanges(false);
+      showToast("Disconnected from AnythingLLM Community Hub", "success");
+      setOriginalConnectionKey("");
+      setConnectionKey("");
+    } catch (error) {
+      console.error(error);
+      showToast("Failed to disconnect from hub", "error");
+    } finally {
+      setLoading(false);
+    }
+  }
+
   useEffect(() => {
     const fetchData = async () => {
       setLoading(true);
@@ -70,6 +90,7 @@ function useCommunityHubAuthentication() {
     updateConnectionKey,
     hasChanges,
     resetChanges,
+    disconnectHub,
   };
 }
 
@@ -82,6 +103,7 @@ export default function CommunityHubAuthentication() {
     updateConnectionKey,
     hasChanges,
     resetChanges,
+    disconnectHub,
   } = useCommunityHubAuthentication();
   if (loading) return <FullScreenLoader />;
   return (
@@ -149,16 +171,26 @@ export default function CommunityHubAuthentication() {
                 className="border-none bg-theme-settings-input-bg text-theme-text-primary placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
                 placeholder="Enter your AnythingLLM Hub API key"
               />
-              <p className="text-theme-text-secondary text-xs mt-2">
-                You can get your API key from your{" "}
-                <a
-                  href={paths.communityHub.profile()}
-                  className="underline text-primary-button"
-                >
-                  AnythingLLM Community Hub profile page
-                </a>
-                .
-              </p>
+              <div className="flex items-center justify-between mt-2">
+                <p className="text-theme-text-secondary text-xs">
+                  You can get your API key from your{" "}
+                  <a
+                    href={paths.communityHub.profile()}
+                    className="underline text-primary-button"
+                  >
+                    AnythingLLM Community Hub profile page
+                  </a>
+                  .
+                </p>
+                {!!originalConnectionKey && (
+                  <button
+                    onClick={disconnectHub}
+                    className="text-red-500 hover:text-red-600 text-sm font-medium transition-colors duration-200"
+                  >
+                    Disconnect
+                  </button>
+                )}
+              </div>
             </div>
           </div>
 
diff --git a/frontend/src/pages/GeneralSettings/CommunityHub/ImportItem/Steps/PullAndReview/HubItem/AgentSkill.jsx b/frontend/src/pages/GeneralSettings/CommunityHub/ImportItem/Steps/PullAndReview/HubItem/AgentSkill.jsx
index b3d949112..c5f10a65a 100644
--- a/frontend/src/pages/GeneralSettings/CommunityHub/ImportItem/Steps/PullAndReview/HubItem/AgentSkill.jsx
+++ b/frontend/src/pages/GeneralSettings/CommunityHub/ImportItem/Steps/PullAndReview/HubItem/AgentSkill.jsx
@@ -150,7 +150,7 @@ function FileReview({ item }) {
         <div className="flex justify-between items-center">
           <button
             type="button"
-            className={`bg-black/70 light:bg-slate-200 rounded-md p-1 text-white/60 light:text-theme-text-secondary text-xs font-mono ${
+            className={`border-none bg-black/70 light:bg-slate-200 rounded-md p-1 text-white/60 light:text-theme-text-secondary text-xs font-mono ${
               index === 0 ? "opacity-50 cursor-not-allowed" : ""
             }`}
             onClick={handlePrevious}
@@ -162,7 +162,7 @@ function FileReview({ item }) {
           </p>
           <button
             type="button"
-            className={`bg-black/70 light:bg-slate-200 rounded-md p-1 text-white/60 light:text-theme-text-secondary text-xs font-mono ${
+            className={`border-none bg-black/70 light:bg-slate-200 rounded-md p-1 text-white/60 light:text-theme-text-secondary text-xs font-mono ${
               index === files.length - 1 ? "opacity-50 cursor-not-allowed" : ""
             }`}
             onClick={handleNext}
@@ -171,11 +171,15 @@ function FileReview({ item }) {
           </button>
         </div>
         <span
-          className="whitespace-pre-line flex flex-col gap-y-1 text-sm leading-[20px] max-h-[500px] overflow-y-auto hljs"
+          className="whitespace-pre-line flex flex-col gap-y-1 text-sm leading-[20px] max-h-[500px] overflow-y-auto hljs text-theme-text-primary"
           dangerouslySetInnerHTML={{
             __html: DOMPurify.sanitize(
               renderMarkdown(
-                `\`\`\`${fileMarkup(file)}\n${file.content}\n\`\`\``
+                `\`\`\`${fileMarkup(file)}\n${
+                  fileMarkup(file) === "markdown"
+                    ? file.content.replace(/```/g, "~~~") // Escape triple backticks in markdown
+                    : file.content
+                }\n\`\`\``
               )
             ),
           }}
-- 
GitLab