Files
supabase/apps/studio/components/layouts/ExplorerLayout/ExplorerNavChats.tsx
Joshen Lim 14bda8a5cf Joshenlim/fe 4438 explorer allow deleting notebookchat without first having to (#50701)
### Context

Adds context menu to the explorer nav to allow users to delete notebooks
or chats from there (instead of having to open the notebook / chat
first, then select delete from the header)
<img width="304" height="339" alt="image"
src="https://github.com/user-attachments/assets/2ad0f21c-0d49-407c-97a2-ef11ae59e280"
/>

### Changes involved:
- Consolidates the rendering of explorer nav items into a single
component that both `ExplorerNavNotebook`, `ExplorerNavHome` and
`ExplorerNavChats` use.
- We were previously rendering a link for notebook, and a button for
chats. But chats' button was just calling `router.push` under the hood.
- Opting to use a Context to handle the delete functionality such that
we can render the confirmation modal just once at the layout level since
there's multiple places that have this delete functionality - notebook
tab, chat tab, context menu in each of the explorer navs

### To test
- [ ] Verify that navigating around the explorer is status quo
- [ ] Verify that you can delete a notebook/chat from within the
notebook/chat tab
- [ ] Verify that you can delete a notebook/chat from the explorer nav
- [ ] Verify that after deleting a notebook/chat from either locations,
the tabs should clear

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Unified chat and notebook navigation with direct links and active-item
highlighting.
- Added context-menu options to delete chats and notebooks with
confirmation and status notifications.
- Added double-click support for keeping recently viewed chats and
notebooks open as tabs.
  - Deleted items and their related tabs are removed automatically.

- **Tests**
- Updated Explorer navigation tests for the unified link-based item
behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-22 16:21:55 +08:00

61 lines
2.2 KiB
TypeScript

import { useParams } from 'common'
import { useRouter } from 'next/router'
import { useState } from 'react'
import { ExplorerNavResourceWrapper } from './ExplorerLayout.constants'
import { ExplorerNavItem } from './ExplorerNavItem'
import { useExplorerDeleteItem } from './ExplorerProvider'
import type { ChatSession } from '@/state/ai-assistant-state'
import { useAiAssistantChatList } from '@/state/ai-assistant-state'
import { createTabId, useTabsStateSnapshot } from '@/state/tabs'
const getVisibleChats = (chats: ChatSession[], search: string): ChatSession[] => {
const normalizedSearch = search.trim().toLowerCase()
return chats
.filter((chat) => !chat.supportMetadata?.isSupportChat)
.filter((chat) => !normalizedSearch || chat.name.toLowerCase().includes(normalizedSearch))
.sort((a, b) => (b.updatedAt?.getTime() ?? 0) - (a.updatedAt?.getTime() ?? 0))
}
export const ExplorerNavChats = () => {
const [search, setSearch] = useState('')
const router = useRouter()
const { id, ref } = useParams()
const chatList = useAiAssistantChatList()
const tabs = useTabsStateSnapshot()
const { onSelectDelete } = useExplorerDeleteItem()
const chats = getVisibleChats(chatList, search)
return (
<ExplorerNavResourceWrapper type="chat" search={search} setSearch={setSearch}>
<div className="flex flex-1 flex-col gap-px overflow-y-auto p-3">
{chats.length === 0 ? (
<p className="px-2 py-2 text-xs text-foreground-lighter">
{search ? 'No chats found' : 'No chats created yet'}
</p>
) : (
chats.map((chat) => {
const isActive = router.pathname.includes('/explorer/chat/') && id === chat.id
return (
<ExplorerNavItem
type="chat"
key={chat.id}
name={chat.name}
isActive={isActive}
href={`/project/${ref}/explorer/chat/${chat.id}`}
onDoubleClick={() => tabs.makeTabPermanent(createTabId('chat', { id: chat.id }))}
onSelectDelete={() =>
onSelectDelete({ id: chat.id, type: 'chat', name: chat.name })
}
/>
)
})
)}
</div>
</ExplorerNavResourceWrapper>
)
}