-
- {/* Connector section - conditional layout based on hasConnections */}
- {hasConnections ? (
-
- {Object.entries(CONNECTORS).map(([provider, config]) => {
- const Icon = config.icon
- const isConnecting =
- connectingProvider === provider ||
- (addConnectionMutation.isPending &&
- addConnectionMutation.variables?.provider === provider)
-
- if (provider === "google-drive") {
- return (
-
-
handleConnect("google-drive")}
- disabled={
- !isProUser ||
- isConnecting ||
- addConnectionMutation.isPending
- }
- className="flex-1 py-3 flex items-center justify-center gap-2 hover:bg-[#1B1F24] transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
- >
-
- {config.title}
- {isConnecting && (
-
- )}
-
-
-
-
-
-
-
-
-
- {(
- Object.entries(GDRIVE_SCOPE_LABELS) as [
- GDriveSyncScope,
- string,
- ][]
- ).map(([scope, label]) => (
- {
- e.stopPropagation()
- setGdriveSyncScope(scope)
- }}
- className="flex items-center justify-between"
- >
- {label}
- {gdriveSyncScope === scope && (
-
- )}
-
- ))}
-
-
-
- )
- }
-
- return (
-
handleConnect(provider as ConnectorProvider)}
- disabled={
- !isProUser || isConnecting || addConnectionMutation.isPending
- }
- className="bg-[#14161A] border border-[rgba(82,89,102,0.2)] rounded-[12px] px-4 py-3 flex items-center justify-center gap-2 hover:bg-[#1B1F24] transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
- >
-
-
- {config.title}
-
- {isConnecting && (
-
- )}
-
- )
- })}
+ {/* Top header — only when empty; once connected, the Add CTA moves into the list header below */}
+ {!hasConnections && (
+
+
Add a connection
+
+ PRO
+
- ) : (
+ )}
+
+ {/* Provider rows — only on empty state. Each is a labelled, descriptive CTA. */}
+ {!hasConnections && (
{Object.entries(CONNECTORS).map(([provider, config]) => {
const Icon = config.icon
- const connection = connections.find(
- (conn) => conn.provider === provider,
- )
- const isConnected = !!connection
const isConnecting =
connectingProvider === provider ||
(addConnectionMutation.isPending &&
@@ -346,33 +411,14 @@ export function ConnectContent({ selectedProject }: ConnectContentProps) {
-
-
{config.title}
- {isConnected && (
-
- {connection.metadata?.syncInProgress
- ? "Syncing..."
- : "Connected"}
-
- )}
-
+
{config.title}
{config.description}
- {isConnected ? (
-
handleDisconnect(connection)}
- disabled={deleteConnectionMutation.isPending}
- className="text-[#737373] hover:text-white hover:bg-[#1B1F24] h-8 w-8 p-0"
- >
-
-
- ) : provider === "google-drive" ? (
+ {provider === "google-drive" ? (
)}
- {/* Connected list panel - only when hasConnections */}
+ {/* Connected list - rich rows with status / project / last sync / doc count */}
{hasConnections && (
-
-
-
- Connected to Supermemory
-
- {connectionsLimit > 0 && (
-
- {connections.length}/{connectionsLimit} connections used
-
- )}
-
-
- {connections.map((connection) => {
- const config =
- CONNECTORS[connection.provider as ConnectorProvider]
- if (!config) return null
-
- const Icon = config.icon
- const subtext = getConnectionSubtext(connection)
+
+
+
+
+
+ Connected to Supermemory
+
+
+ PRO
+
+
+ {connectionsLimit > 0 && (
+
+ {connections.length}/{connectionsLimit} connections used
+
+ )}
+
- return (
-
+
+
-
-
-
-
- {config.title}
-
-
- {subtext}
-
-
+ {isAnyConnecting ? (
+
+ ) : (
+ <>
+
+ Add a connection
+
+ >
+ )}
+
+
+
+
+
+
+ Choose a service
+
+
+
+
{
+ setConnectingProvider("google-drive")
+ addConnectionMutation.mutate({
+ provider: "google-drive",
+ syncScope: "scoped",
+ })
+ }}
+ className="flex items-start gap-2.5 px-3 py-2.5 rounded-md cursor-pointer text-white opacity-60 hover:opacity-100 hover:bg-[#293952]/40 focus:bg-[#293952]/40 focus:opacity-100"
+ >
+
+
+
+ Google Drive
+
+
+ Pick specific files & folders
+
+
+
+
{
+ setConnectingProvider("google-drive")
+ addConnectionMutation.mutate({
+ provider: "google-drive",
+ syncScope: "full",
+ })
+ }}
+ className="flex items-start gap-2.5 px-3 py-2.5 rounded-md cursor-pointer text-white opacity-60 hover:opacity-100 hover:bg-[#293952]/40 focus:bg-[#293952]/40 focus:opacity-100"
+ >
+
+
+
+ Google Drive
+
+
+ Sync entire drive
+
+
+
+
{
+ setConnectingProvider("notion")
+ addConnectionMutation.mutate({ provider: "notion" })
+ }}
+ className="flex items-start gap-2.5 px-3 py-2.5 rounded-md cursor-pointer text-white opacity-60 hover:opacity-100 hover:bg-[#293952]/40 focus:bg-[#293952]/40 focus:opacity-100"
+ >
+
+
+
+ Notion
+
+
+ Pages and databases
+
+
+
+
{
+ setConnectingProvider("onedrive")
+ addConnectionMutation.mutate({ provider: "onedrive" })
+ }}
+ className="flex items-start gap-2.5 px-3 py-2.5 rounded-md cursor-pointer text-white opacity-60 hover:opacity-100 hover:bg-[#293952]/40 focus:bg-[#293952]/40 focus:opacity-100"
+ >
+
+
+
+ OneDrive
+
+
+ Office documents
+
+
+
-
handleDisconnect(connection)}
- disabled={deleteConnectionMutation.isPending}
- className="text-[#737373] hover:text-white hover:bg-[#1B1F24] h-8 w-8 p-0 shrink-0"
- >
-
-
- )
- })}
+
+
+
+
+ {connections.map((connection) => (
+ setRemoveDialog({ open: true, connection })}
+ isDeleting={deleteConnectionMutation.isPending}
+ />
+ ))}
)}
@@ -568,6 +708,7 @@ export function ConnectContent({ selectedProject }: ConnectContentProps) {
)}
)}
+
{
diff --git a/apps/web/components/dashboard-view.tsx b/apps/web/components/dashboard-view.tsx
index be611c3fa..9652f5eab 100644
--- a/apps/web/components/dashboard-view.tsx
+++ b/apps/web/components/dashboard-view.tsx
@@ -389,10 +389,14 @@ function MemoryOfDayCard({ data }: { data: MemoryOfDay }) {
if (!memory) return null
+ const href = data.sourceDocumentId
+ ? `/?view=list&doc=${encodeURIComponent(data.sourceDocumentId)}`
+ : "/?view=list"
+
return (
router.push("/?view=list")}
+ onClick={() => router.push(href)}
className={cn(
"group w-full h-full text-left bg-[#0B1017] border border-[rgba(255,255,255,0.05)] rounded-[18px] p-3 flex flex-col justify-between hover:border-[rgba(255,255,255,0.10)] transition-colors cursor-pointer",
dmSansClassName(),
diff --git a/apps/web/components/integrations-view.tsx b/apps/web/components/integrations-view.tsx
index 55e1215a1..ab8b0b1b5 100644
--- a/apps/web/components/integrations-view.tsx
+++ b/apps/web/components/integrations-view.tsx
@@ -23,7 +23,8 @@ import { analytics } from "@/lib/analytics"
import Image from "next/image"
import { IntegrationGridCard } from "@/components/integrations/integration-grid-card"
import { useViewMode } from "@/lib/view-mode-context"
-import type { ViewParamValue } from "@/lib/search-params"
+import { addDocumentParam, type ViewParamValue } from "@/lib/search-params"
+import { useQueryState } from "nuqs"
type Connection = z.infer
@@ -166,6 +167,7 @@ const CARD_GROUPS: Array<{ label: string; ids: CardId[] }> = [
export function IntegrationsView() {
const { setViewMode } = useViewMode()
+ const [, setAddDoc] = useQueryState("add", addDocumentParam)
const { org } = useAuth()
const autumn = useCustomer()
const hasProProduct = hasActivePlan(autumn.customer?.products, "api_pro")
@@ -290,6 +292,8 @@ export function IntegrationsView() {
analytics.onboardingChromeExtensionClicked({
source: "integrations",
})
+ } else if (card.id === "connections") {
+ void setAddDoc("connect")
} else {
void setViewMode(card.id as ViewParamValue)
}
diff --git a/apps/web/components/integrations/connections-detail.tsx b/apps/web/components/integrations/connections-detail.tsx
deleted file mode 100644
index 5ae871781..000000000
--- a/apps/web/components/integrations/connections-detail.tsx
+++ /dev/null
@@ -1,442 +0,0 @@
-"use client"
-
-import { dmSans125ClassName } from "@/lib/fonts"
-import { cn } from "@lib/utils"
-import { $fetch } from "@lib/api"
-import { hasActivePlan } from "@lib/queries"
-import { GoogleDrive, Notion, OneDrive } from "@ui/assets/icons"
-import { useCustomer } from "autumn-js/react"
-import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
-import { Check, Clock, FolderOpen, Plus, Trash2, Zap } from "lucide-react"
-import { useEffect, useState } from "react"
-import { toast } from "sonner"
-import { useQueryState } from "nuqs"
-import type { ConnectionResponseSchema } from "@repo/validation/api"
-import type { z } from "zod"
-import { analytics } from "@/lib/analytics"
-import { AddDocumentModal } from "@/components/add-document"
-import { RemoveConnectionDialog } from "@/components/remove-connection-dialog"
-import { addDocumentParam } from "@/lib/search-params"
-import { DEFAULT_PROJECT_ID } from "@lib/constants"
-import type { Project } from "@lib/types"
-
-type Connection = z.infer
-
-const CONNECTORS = {
- "google-drive": {
- title: "Google Drive",
- icon: GoogleDrive,
- documentLabel: "documents",
- },
- notion: { title: "Notion", icon: Notion, documentLabel: "pages" },
- onedrive: { title: "OneDrive", icon: OneDrive, documentLabel: "documents" },
-} as const
-
-type ConnectorProvider = keyof typeof CONNECTORS
-
-function ConnectionRow({
- connection,
- onDelete,
- isDeleting,
- disabled,
- projects,
-}: {
- connection: Connection
- onDelete: () => void
- isDeleting: boolean
- disabled?: boolean
- projects: Project[]
-}) {
- const config = CONNECTORS[connection.provider as ConnectorProvider]
- if (!config) return null
-
- const Icon = config.icon
- const isConnected =
- !connection.expiresAt || new Date(connection.expiresAt) > new Date()
-
- const formatRelativeTime = (date: string | null | undefined) => {
- if (!date) return "Never"
- const d = new Date(date)
- const diffMs = Date.now() - d.getTime()
- const diffHours = Math.floor(diffMs / (1000 * 60 * 60))
- const diffDays = Math.floor(diffHours / 24)
- if (diffHours < 1) return "Just now"
- if (diffHours < 24) return `${diffHours}h ago`
- if (diffDays === 1) return "Yesterday"
- if (diffDays < 7) return `${diffDays} days ago`
- return d.toLocaleDateString()
- }
-
- const getProjectName = (tag: string): string => {
- if (tag === DEFAULT_PROJECT_ID) return "Default"
- return (
- projects.find((p) => p.containerTag === tag)?.name ??
- tag.replace(/^sm_project_/, "").replace(/_/g, " ")
- )
- }
-
- const documentCount = (connection.metadata?.documentCount as number) ?? 0
- const containerTags = (
- connection as Connection & { containerTags?: string[] }
- ).containerTags
- const projectName = containerTags?.[0]
- ? getProjectName(containerTags[0])
- : null
-
- return (
-
-
-
-
-
-
-
- {config.title}
-
-
-
-
- {isConnected ? "Connected" : "Disconnected"}
-
-
-
-
- {connection.email || "Unknown"}
-
-
-
-
-
-
-
-
- {projectName && (
-
-
-
- {projectName}
-
-
- )}
-
-
-
- {formatRelativeTime(connection.createdAt)}
-
-
-
-
-
- {documentCount}
-
-
- {config.documentLabel}
-
-
-
-
-
- )
-}
-
-export function ConnectionsDetail() {
- const queryClient = useQueryClient()
- const autumn = useCustomer()
- const [isAddDocumentOpen, setIsAddDocumentOpen] = useState(false)
- const [removeDialog, setRemoveDialog] = useState<{
- open: boolean
- connection: Connection | null
- }>({ open: false, connection: null })
- const [, setAddDoc] = useQueryState("add", addDocumentParam)
-
- const projects = (queryClient.getQueryData(["projects"]) ||
- []) as Project[]
-
- const hasProProduct = hasActivePlan(autumn.customer?.products, "api_pro")
-
- const connectionsFeature = autumn.customer?.features?.connections
- const connectionsUsed = connectionsFeature?.usage ?? 0
- const connectionsLimit = connectionsFeature?.included_usage ?? 10
- const canAddConnection = connectionsUsed < connectionsLimit
-
- const {
- data: connections = [],
- isLoading: isLoadingConnections,
- error: connectionsError,
- } = useQuery({
- queryKey: ["connections"],
- queryFn: async () => {
- const response = await $fetch("@post/connections/list", {
- body: { containerTags: [] },
- })
- if (response.error)
- throw new Error(response.error?.message || "Failed to load connections")
- return response.data as Connection[]
- },
- staleTime: 30 * 1000,
- refetchInterval: 60 * 1000,
- enabled: hasProProduct,
- })
-
- useEffect(() => {
- if (connectionsError) {
- toast.error("Failed to load connections", {
- description:
- connectionsError instanceof Error
- ? connectionsError.message
- : "Unknown error",
- })
- }
- }, [connectionsError])
-
- const deleteConnectionMutation = useMutation({
- mutationFn: async ({
- connectionId,
- deleteDocuments,
- }: {
- connectionId: string
- deleteDocuments: boolean
- }) => {
- await $fetch(`@delete/connections/${connectionId}`, {
- query: { deleteDocuments },
- })
- return { deleteDocuments }
- },
- onSuccess: (_data, variables) => {
- analytics.connectionDeleted()
- toast.success(
- variables.deleteDocuments
- ? "Connection removal has started. Documents will be permanently deleted in the next few minutes."
- : "Connection removed. Your memories have been kept.",
- )
- setRemoveDialog({ open: false, connection: null })
- queryClient.invalidateQueries({ queryKey: ["connections"] })
- },
- onError: (error) => {
- toast.error("Failed to remove connection", {
- description: error instanceof Error ? error.message : "Unknown error",
- })
- },
- })
-
- const handleUpgrade = async () => {
- try {
- await autumn.attach({
- productId: "api_pro",
- successUrl: "https://app.supermemory.ai/?view=integrations",
- })
- window.location.reload()
- } catch (error) {
- console.error(error)
- }
- }
-
- const isLoading = autumn.isLoading
-
- return (
-
- {!hasProProduct && !isLoading && (
- <>
-
-
-
-
-
- Connect Google Drive, Notion, and OneDrive to import your
- knowledge
-
-
- {[
- "Unlimited memories",
- "10 connections",
- "Advanced search",
- "Priority support",
- ].map((text) => (
-
-
-
- {text}
-
-
- ))}
-
-
- Upgrade to Pro
-
-
-
- >
- )}
-
-
-
-
- Connected to Supermemory
-
-
- {connections.length}/{connectionsLimit} connections used
-
-
-
- {isLoadingConnections ? (
-
- ) : connections.length > 0 ? (
- connections.map((connection) => (
-
setRemoveDialog({ open: true, connection })}
- isDeleting={deleteConnectionMutation.isPending}
- disabled={!hasProProduct}
- projects={projects}
- />
- ))
- ) : (
-
-
-
- No connections yet
-
-
- Connect a service below to import your knowledge
-
-
- )}
-
- setIsAddDocumentOpen(false)}
- />
-
- {
- if (!open) setRemoveDialog({ open: false, connection: null })
- }}
- provider={removeDialog.connection?.provider}
- documentCount={
- (removeDialog.connection?.metadata?.documentCount as number) ?? 0
- }
- onConfirm={(deleteDocuments) => {
- if (removeDialog.connection) {
- deleteConnectionMutation.mutate({
- connectionId: removeDialog.connection.id,
- deleteDocuments,
- })
- }
- }}
- isDeleting={deleteConnectionMutation.isPending}
- />
-
- setAddDoc("connect")}
- disabled={!hasProProduct || !canAddConnection}
- className={cn(
- "relative flex items-center justify-center gap-2",
- "bg-[#0D121A] rounded-full h-11 px-4 w-full",
- "cursor-pointer transition-opacity hover:opacity-80",
- "shadow-[inset_1.5px_1.5px_4.5px_rgba(0,0,0,0.7)]",
- "disabled:opacity-50 disabled:cursor-not-allowed",
- dmSans125ClassName(),
- )}
- >
-
-
- Connect knowledge bases
-
-
-
-
- )
-}
diff --git a/apps/web/components/memories-grid.tsx b/apps/web/components/memories-grid.tsx
index 0c8a2be39..3127c148d 100644
--- a/apps/web/components/memories-grid.tsx
+++ b/apps/web/components/memories-grid.tsx
@@ -45,6 +45,7 @@ import {
} from "@ui/components/alert-dialog"
import {
AlignLeft,
+ BoxSelect,
CheckIcon,
LayoutGrid,
Loader,
@@ -180,7 +181,13 @@ function MemoriesGridLoading() {
// Discriminated union for masonry items
type MasonryItem =
- | { type: "document"; id: string; data: DocumentWithMemories }
+ | {
+ type: "document"
+ id: string
+ data: DocumentWithMemories
+ isSelectionMode: boolean
+ isSelected: boolean
+ }
| { type: "quick-note"; id: "quick-note" }
interface QuickNoteProps {
@@ -369,11 +376,17 @@ export function MemoriesGrid({
}
for (const doc of documents) {
- items.push({ type: "document", id: doc.id, data: doc })
+ items.push({
+ type: "document",
+ id: doc.id,
+ data: doc,
+ isSelectionMode,
+ isSelected: doc.id ? selectedDocumentIds.has(doc.id) : false,
+ })
}
return items
- }, [documents, isMobile, hasQuickNote])
+ }, [documents, isMobile, hasQuickNote, isSelectionMode, selectedDocumentIds])
// Stable key for Masonry based on document IDs, not item values
const masonryKey = useMemo(() => {
@@ -437,16 +450,12 @@ export function MemoriesGrid({
const renderRef = useRef({
quickNoteProps,
handleCardClick,
- isSelectionMode,
- selectedDocumentIds,
onToggleSelection,
processingStatusMap,
})
renderRef.current = {
quickNoteProps,
handleCardClick,
- isSelectionMode,
- selectedDocumentIds,
onToggleSelection,
processingStatusMap,
}
@@ -480,8 +489,8 @@ export function MemoriesGrid({
data={doc}
width={width}
onClick={r.handleCardClick}
- isSelectionMode={r.isSelectionMode}
- isSelected={doc.id ? r.selectedDocumentIds.has(doc.id) : false}
+ isSelectionMode={data.isSelectionMode}
+ isSelected={data.isSelected}
onToggleSelection={
doc.id && r.onToggleSelection
? () => r.onToggleSelection?.(doc.id as string)
@@ -518,12 +527,16 @@ export function MemoriesGrid({
const isEmpty = documents.length === 0 && !isPending
const showNovaEmptyState = isEmpty && emptyStateProps
+ const allVisibleSelected =
+ documents.length > 0 &&
+ documents.every((d) => d.id && selectedDocumentIds.has(d.id))
+
return (
- {!isEmpty && (
+ {!isEmpty && !isSelectionMode && (
- {/* View mode toggle */}
-
+ {/* View mode toggle — segmented control */}
+
handleSetViewMode("grid")}
>
-
+
+ Grid
handleSetViewMode("timeline")}
>
-
+
+ Timeline
- {isSelectionMode && (
- <>
-
-
-
- {selectedDocumentIds.size > 0 ? (
- <>
-
- Select all
-
-
-
- Delete ({selectedDocumentIds.size})
-
- >
- ) : (
-
- Select one or more documents
-
- )}
- >
- )}
- {!isSelectionMode && onEnterSelectionMode && (
+ {onEnterSelectionMode && (
-
+
)}
)}
+ {!isEmpty && isSelectionMode && (
+
+ )}
+
("keep")
+ const [alsoDelete, setAlsoDelete] = useState(false)
const displayName =
providerName || (provider ? PROVIDER_LABELS[provider] : "this connection")
+ const memoryNoun = documentCount === 1 ? "memory" : "memories"
+ const hasMemories = documentCount > 0
+
const handleConfirm = () => {
- onConfirm(action === "delete")
+ onConfirm(alsoDelete)
}
return (
@@ -56,13 +60,13 @@ export function RemoveConnectionDialog({
onOpenChange={(o) => {
if (!isDeleting) {
onOpenChange(o)
- if (!o) setAction("keep")
+ if (!o) setAlsoDelete(false)
}
}}
>
-
-
-
-
- Remove connection
-
-
- What would you like to do with the{" "}
- {documentCount > 0 ? (
- <>
-
- {documentCount}
- {" "}
- memories from{" "}
- >
- ) : (
- <>memories from >
- )}
-
- {displayName}
-
- ?
-
-
+
+
+
+ Disconnect {displayName}?
+
-
-
setAction("keep")}
- className={cn(
- "flex items-center gap-3 p-3 rounded-[12px] cursor-pointer transition-colors w-full text-left",
- action === "keep"
- ? "bg-[#14161A] border border-[rgba(82,89,102,0.3)]"
- : "bg-[#14161A]/50 border border-transparent hover:border-[rgba(82,89,102,0.2)]",
- )}
- style={{
- boxShadow:
- action === "keep"
- ? "0px 1px 2px 0px rgba(0,43,87,0.1), inset 0px 0px 0px 1px rgba(43,49,67,0.08), inset 0px 1px 1px 0px rgba(0,0,0,0.08)"
- : "none",
- }}
- >
-
- {action === "keep" && (
-
- )}
-
-
-
- Remove connection only
-
-
- Disconnect the integration but keep all imported memories
-
-
-
+
+ {hasMemories ? (
+ <>
+ Sync stops. Your{" "}
+
+ {documentCount} {memoryNoun}
+ {" "}
+ stay in Supermemory.
+ >
+ ) : (
+ <>Sync stops. No memories were imported from this connection.>
+ )}
+
-
setAction("delete")}
- className={cn(
- "flex items-center gap-3 p-3 rounded-[12px] cursor-pointer transition-colors w-full text-left",
- action === "delete"
- ? "bg-[#14161A] border border-[rgba(220,38,38,0.3)]"
- : "bg-[#14161A]/50 border border-transparent hover:border-[rgba(82,89,102,0.2)]",
- )}
- style={{
- boxShadow:
- action === "delete"
- ? "0px 1px 2px 0px rgba(87,0,0,0.1), inset 0px 0px 0px 1px rgba(67,43,43,0.08), inset 0px 1px 1px 0px rgba(0,0,0,0.08)"
- : "none",
- }}
+ {hasMemories && (
+
-
- {action === "delete" && (
-
- )}
-
-
-
- Remove connection and memories
-
-
- Permanently delete all memories imported from this connection
-
-
-
-
+
setAlsoDelete(checked === true)}
+ disabled={isDeleting}
+ />
+
+ Also delete the {documentCount} imported {memoryNoun}{" "}
+ (optional)
+
+
+ )}
{
onOpenChange(false)
- setAction("keep")
+ setAlsoDelete(false)
}}
className="text-[#737373] cursor-pointer rounded-full"
>
@@ -203,19 +146,18 @@ export function RemoveConnectionDialog({
disabled={isDeleting}
onClick={handleConfirm}
className={cn(
- action === "delete" &&
- "bg-red-600! hover:bg-red-700! text-white",
+ alsoDelete && "bg-red-600! hover:bg-red-700! text-white",
)}
>
{isDeleting ? (
<>
- Removing...
+ Disconnecting...
>
- ) : action === "delete" ? (
- "Remove & delete memories"
+ ) : alsoDelete ? (
+ `Disconnect and delete ${memoryNoun}`
) : (
- "Remove connection"
+ "Disconnect"
)}
diff --git a/apps/web/stores/index.ts b/apps/web/stores/index.ts
index 5379b06ee..4d754f773 100644
--- a/apps/web/stores/index.ts
+++ b/apps/web/stores/index.ts
@@ -13,7 +13,8 @@ export function useProject() {
const selectedProject = selectedProjects[0] ?? DEFAULT_PROJECT_ID
- const effectiveContainerTags = selectedProjects
+ const effectiveContainerTags =
+ selectedProjects.length === 0 ? [DEFAULT_PROJECT_ID] : selectedProjects
const setSelectedProjects = useCallback(
(projects: string[]) => {