diff --git a/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts b/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts
index 8cbf08bb9..731a55c3f 100644
--- a/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts
+++ b/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts
@@ -316,44 +316,46 @@ export const useConnectorDialog = () => {
}
}
}
- } else {
- setIsOpen(false);
- // Clear indexing config when modal is closed
- if (indexingConfig) {
- setIndexingConfig(null);
- setIndexingConnector(null);
- setIndexingConnectorConfig(null);
- setStartDate(undefined);
- setEndDate(undefined);
- setPeriodicEnabled(false);
- setFrequencyMinutes("1440");
- setEnableSummary(false);
- setIsScrolled(false);
- setSearchQuery("");
- }
- // Clear editing connector when modal is closed
- if (editingConnector) {
- setEditingConnector(null);
- setConnectorName(null);
- setConnectorConfig(null);
- setStartDate(undefined);
- setEndDate(undefined);
- setPeriodicEnabled(false);
- setFrequencyMinutes("1440");
- setEnableSummary(false);
- setIsScrolled(false);
- setSearchQuery("");
- }
- // Clear connecting connector type when modal is closed
- if (connectingConnectorType) {
- setConnectingConnectorType(null);
- }
- // Clear viewing accounts type when modal is closed
- if (viewingAccountsType) {
- setViewingAccountsType(null);
- }
- // Clear YouTube view when modal is closed (handled by view param check)
+ } else {
+ // Do NOT call setIsOpen(false) here. Closing the dialog is handled
+ // explicitly by handleOpenChange and the individual action handlers.
+ // Relying on URL params to close the dialog caused a race condition
+ // where Next.js router updates from tab switches briefly produced
+ // stale searchParams without the "modal" key, closing the popup.
+
+ // Still clean up sub-view state when the modal param is gone
+ // (e.g. after browser back navigation or explicit handler URL cleanup).
+ if (indexingConfig) {
+ setIndexingConfig(null);
+ setIndexingConnector(null);
+ setIndexingConnectorConfig(null);
+ setStartDate(undefined);
+ setEndDate(undefined);
+ setPeriodicEnabled(false);
+ setFrequencyMinutes("1440");
+ setEnableSummary(false);
+ setIsScrolled(false);
+ setSearchQuery("");
}
+ if (editingConnector) {
+ setEditingConnector(null);
+ setConnectorName(null);
+ setConnectorConfig(null);
+ setStartDate(undefined);
+ setEndDate(undefined);
+ setPeriodicEnabled(false);
+ setFrequencyMinutes("1440");
+ setEnableSummary(false);
+ setIsScrolled(false);
+ setSearchQuery("");
+ }
+ if (connectingConnectorType) {
+ setConnectingConnectorType(null);
+ }
+ if (viewingAccountsType) {
+ setViewingAccountsType(null);
+ }
+ }
} catch (error) {
// Invalid query params - log but don't crash
console.warn("Invalid connector popup query params:", error);
@@ -410,17 +412,18 @@ export const useConnectorDialog = () => {
COMPOSIO_CONNECTORS.find((c) => c.id === params.connector)
: null;
- if (earlyConnector && AUTO_INDEX_CONNECTOR_TYPES.has(earlyConnector.connectorType)) {
- toast.loading(`Setting up ${earlyConnector.title}...`, { id: "auto-index" });
- const url = new URL(window.location.href);
- url.searchParams.delete("success");
- url.searchParams.delete("connector");
- url.searchParams.delete("connectorId");
- url.searchParams.delete("view");
- url.searchParams.delete("modal");
- url.searchParams.delete("tab");
- router.replace(url.pathname + url.search, { scroll: false });
- }
+ if (earlyConnector && AUTO_INDEX_CONNECTOR_TYPES.has(earlyConnector.connectorType)) {
+ toast.loading(`Setting up ${earlyConnector.title}...`, { id: "auto-index" });
+ setIsOpen(false);
+ const url = new URL(window.location.href);
+ url.searchParams.delete("success");
+ url.searchParams.delete("connector");
+ url.searchParams.delete("connectorId");
+ url.searchParams.delete("view");
+ url.searchParams.delete("modal");
+ url.searchParams.delete("tab");
+ router.replace(url.pathname + url.search, { scroll: false });
+ }
refetchAllConnectors().then(async (result) => {
if (!result.data) {
@@ -789,20 +792,22 @@ export const useConnectorDialog = () => {
},
});
- const successMessage =
- currentConnectorType === "MCP_CONNECTOR"
- ? `${connector.name} added successfully`
- : `${connectorTitle} connected and syncing started!`;
- toast.success(successMessage);
+ const successMessage =
+ currentConnectorType === "MCP_CONNECTOR"
+ ? `${connector.name} added successfully`
+ : `${connectorTitle} connected and syncing started!`;
+ toast.success(successMessage);
- const url = new URL(window.location.href);
- url.searchParams.delete("modal");
- url.searchParams.delete("tab");
- url.searchParams.delete("view");
- url.searchParams.delete("connectorType");
- router.replace(url.pathname + url.search, { scroll: false });
+ // Close dialog and clean up URL
+ setIsOpen(false);
+ const url = new URL(window.location.href);
+ url.searchParams.delete("modal");
+ url.searchParams.delete("tab");
+ url.searchParams.delete("view");
+ url.searchParams.delete("connectorType");
+ router.replace(url.pathname + url.search, { scroll: false });
- // Clear indexing config state since we're not showing the view
+ // Clear indexing config state since we're not showing the view
setIndexingConfig(null);
setIndexingConnector(null);
setIndexingConnectorConfig(null);
@@ -850,23 +855,24 @@ export const useConnectorDialog = () => {
// Refresh connectors list
await refetchAllConnectors();
} else {
- // Other non-indexable connectors - just show success message and close
- const successMessage =
- currentConnectorType === "MCP_CONNECTOR"
- ? `${connector.name} added successfully`
- : `${connectorTitle} connected successfully!`;
- toast.success(successMessage);
+ // Other non-indexable connectors - just show success message and close
+ const successMessage =
+ currentConnectorType === "MCP_CONNECTOR"
+ ? `${connector.name} added successfully`
+ : `${connectorTitle} connected successfully!`;
+ toast.success(successMessage);
- // Refresh connectors list before closing modal
- await refetchAllConnectors();
+ // Refresh connectors list before closing modal
+ await refetchAllConnectors();
- // Close modal and return to main view
- const url = new URL(window.location.href);
- url.searchParams.delete("modal");
- url.searchParams.delete("tab");
- url.searchParams.delete("view");
- url.searchParams.delete("connectorType");
- router.replace(url.pathname + url.search, { scroll: false });
+ // Close dialog and clean up URL
+ setIsOpen(false);
+ const url = new URL(window.location.href);
+ url.searchParams.delete("modal");
+ url.searchParams.delete("tab");
+ url.searchParams.delete("view");
+ url.searchParams.delete("connectorType");
+ router.replace(url.pathname + url.search, { scroll: false });
// Clear indexing config state
setIndexingConfig(null);
@@ -894,6 +900,7 @@ export const useConnectorDialog = () => {
updateConnector,
indexConnector,
router,
+ setIsOpen,
]
);
@@ -1122,18 +1129,19 @@ export const useConnectorDialog = () => {
);
}
- toast.success(`${indexingConfig.connectorTitle} indexing started`);
+ toast.success(`${indexingConfig.connectorTitle} indexing started`);
- // Update URL - the effect will handle closing the modal and clearing state
- const url = new URL(window.location.href);
- url.searchParams.delete("modal");
- url.searchParams.delete("tab");
- url.searchParams.delete("success");
- url.searchParams.delete("connector");
- url.searchParams.delete("view");
- router.replace(url.pathname + url.search, { scroll: false });
+ // Close dialog and clean up URL
+ setIsOpen(false);
+ const url = new URL(window.location.href);
+ url.searchParams.delete("modal");
+ url.searchParams.delete("tab");
+ url.searchParams.delete("success");
+ url.searchParams.delete("connector");
+ url.searchParams.delete("view");
+ router.replace(url.pathname + url.search, { scroll: false });
- refreshConnectors();
+ refreshConnectors();
queryClient.invalidateQueries({
queryKey: cacheKeys.logs.summary(Number(searchSpaceId)),
});
@@ -1156,12 +1164,14 @@ export const useConnectorDialog = () => {
enableSummary,
router,
indexingConnectorConfig,
+ setIsOpen,
]
);
// Handle skipping indexing
const handleSkipIndexing = useCallback(() => {
- // Update URL - the effect will handle closing the modal and clearing state
+ // Close dialog and clean up URL
+ setIsOpen(false);
const url = new URL(window.location.href);
url.searchParams.delete("modal");
url.searchParams.delete("tab");
@@ -1169,7 +1179,7 @@ export const useConnectorDialog = () => {
url.searchParams.delete("connector");
url.searchParams.delete("view");
router.replace(url.pathname + url.search, { scroll: false });
- }, [router]);
+ }, [router, setIsOpen]);
// Handle starting edit mode
const handleStartEdit = useCallback(
@@ -1411,41 +1421,43 @@ export const useConnectorDialog = () => {
: indexingDescription,
});
- // Update URL - the effect will handle closing the modal and clearing state
- const url = new URL(window.location.href);
- url.searchParams.delete("modal");
- url.searchParams.delete("tab");
- url.searchParams.delete("view");
- url.searchParams.delete("connectorId");
- router.replace(url.pathname + url.search, { scroll: false });
+ // Close dialog and clean up URL
+ setIsOpen(false);
+ const url = new URL(window.location.href);
+ url.searchParams.delete("modal");
+ url.searchParams.delete("tab");
+ url.searchParams.delete("view");
+ url.searchParams.delete("connectorId");
+ router.replace(url.pathname + url.search, { scroll: false });
- refreshConnectors();
- queryClient.invalidateQueries({
- queryKey: cacheKeys.logs.summary(Number(searchSpaceId)),
- });
- } catch (error) {
- console.error("Error saving connector:", error);
- toast.error("Failed to save connector changes");
- } finally {
- setIsSaving(false);
- }
- },
- [
- editingConnector,
- searchSpaceId,
- isSaving,
- startDate,
- endDate,
- indexConnector,
- updateConnector,
- periodicEnabled,
- frequencyMinutes,
- enableSummary,
- getFrequencyLabel,
- router,
- connectorConfig,
- connectorName,
- ]
+ refreshConnectors();
+ queryClient.invalidateQueries({
+ queryKey: cacheKeys.logs.summary(Number(searchSpaceId)),
+ });
+ } catch (error) {
+ console.error("Error saving connector:", error);
+ toast.error("Failed to save connector changes");
+ } finally {
+ setIsSaving(false);
+ }
+ },
+ [
+ editingConnector,
+ searchSpaceId,
+ isSaving,
+ startDate,
+ endDate,
+ indexConnector,
+ updateConnector,
+ periodicEnabled,
+ frequencyMinutes,
+ enableSummary,
+ getFrequencyLabel,
+ router,
+ connectorConfig,
+ connectorName,
+ setIsOpen,
+ ]
);
// Handle disconnecting connector
@@ -1472,35 +1484,36 @@ export const useConnectorDialog = () => {
: `${editingConnector.name} disconnected successfully`
);
- // Update URL - for MCP from list view, go back to list; otherwise close modal
- const url = new URL(window.location.href);
- if (editingConnector.connector_type === "MCP_CONNECTOR" && cameFromMCPList) {
- // Go back to MCP list view only if we came from there
- setViewingMCPList(true);
- url.searchParams.set("modal", "connectors");
- url.searchParams.set("view", "mcp-list");
- url.searchParams.delete("connectorId");
- } else {
- // Close modal for all other cases
- url.searchParams.delete("modal");
- url.searchParams.delete("tab");
- url.searchParams.delete("view");
- url.searchParams.delete("connectorId");
- }
- router.replace(url.pathname + url.search, { scroll: false });
-
- refreshConnectors();
- queryClient.invalidateQueries({
- queryKey: cacheKeys.logs.summary(Number(searchSpaceId)),
- });
- } catch (error) {
- console.error("Error disconnecting connector:", error);
- toast.error("Failed to disconnect connector");
- } finally {
- setIsDisconnecting(false);
+ // Update URL - for MCP from list view, go back to list; otherwise close modal
+ const url = new URL(window.location.href);
+ if (editingConnector.connector_type === "MCP_CONNECTOR" && cameFromMCPList) {
+ // Go back to MCP list view only if we came from there
+ setViewingMCPList(true);
+ url.searchParams.set("modal", "connectors");
+ url.searchParams.set("view", "mcp-list");
+ url.searchParams.delete("connectorId");
+ } else {
+ // Close dialog for all other cases
+ setIsOpen(false);
+ url.searchParams.delete("modal");
+ url.searchParams.delete("tab");
+ url.searchParams.delete("view");
+ url.searchParams.delete("connectorId");
}
- },
- [editingConnector, searchSpaceId, deleteConnector, router, cameFromMCPList]
+ router.replace(url.pathname + url.search, { scroll: false });
+
+ refreshConnectors();
+ queryClient.invalidateQueries({
+ queryKey: cacheKeys.logs.summary(Number(searchSpaceId)),
+ });
+ } catch (error) {
+ console.error("Error disconnecting connector:", error);
+ toast.error("Failed to disconnect connector");
+ } finally {
+ setIsDisconnecting(false);
+ }
+ },
+ [editingConnector, searchSpaceId, deleteConnector, router, cameFromMCPList, setIsOpen]
);
// Handle quick index (index with selected date range, or backend defaults if none selected)
diff --git a/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx b/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx
index 0c7181ee0..88bad2a27 100644
--- a/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx
+++ b/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx
@@ -9,7 +9,7 @@ import {
Laptop,
LogOut,
Moon,
- Settings,
+ UserCog,
Sun,
} from "lucide-react";
import Image from "next/image";
@@ -206,7 +206,7 @@ export function SidebarUserProfile({
-
+
{t("user_settings")}
@@ -351,7 +351,7 @@ export function SidebarUserProfile({
-
+
{t("user_settings")}