feat: connect search and project settings

This commit is contained in:
2026-07-21 17:16:45 +08:00
parent bc1cd19c51
commit 110423406b
15 changed files with 825 additions and 139 deletions

View File

@@ -11,15 +11,18 @@ import {
createTask,
fetchProjectWorkspace,
fetchProjects,
updateProject,
updateTask,
uploadSource,
} from '../api/projects'
import type { SearchResultDTO } from '../api/search'
import { LoginPage } from '../pages/login'
import { ProjectActionModals, type CronDraft, type ProjectActionModal, type ProjectDraft, type SourceDraft, type TaskDraft } from '../pages/projects/project-action-modals'
import type { ProjectSettingsUpdate } from '../pages/projects/project-sidebar'
import { ProjectPage } from '../pages/workspace-home'
import type { WorkspaceTaskUpdate } from '../pages/workspace-body'
import type { ChannelKey, Project, ProjectWorkspace, Screen, Theme, WorkbenchView } from '../pages/projects/project-types'
import { useWorkbenchSearch } from './use-workbench-search'
function App() {
const [screen, setScreen] = useState<Screen>('login')
@@ -34,6 +37,7 @@ function App() {
const [loading, setLoading] = useState(false)
const [actionLoading, setActionLoading] = useState(false)
const [activeModal, setActiveModal] = useState<ProjectActionModal>(null)
const workspaceSearch = useWorkbenchSearch(session)
const dark = theme === 'dark'
const activeWorkspace = workspaces.find((workspace) => workspace.project.id === activeProjectID) ?? workspaces[0]
@@ -210,23 +214,35 @@ function App() {
}, '任务已更新')
}
function handleUpdateProject(update: ProjectSettingsUpdate) {
setWorkspaces((current) =>
current.map((workspace) => {
if (workspace.project.id !== update.projectId) return workspace
const nextProject = {
...workspace.project,
name: update.name,
identifier: update.identifier,
icon: update.icon,
background: update.background,
description: update.description,
short: projectIconLabel(update.icon, update.identifier || update.name.slice(0, 2)),
color: projectColor(update.background, workspace.project.color),
}
return { ...workspace, project: nextProject }
}),
)
async function handleUpdateProject(update: ProjectSettingsUpdate) {
const currentSession = requireSession()
await updateProject(currentSession, update.projectId, {
name: update.name,
identifier: update.identifier,
icon: update.icon,
background: update.background,
description: update.description,
})
await loadWorkspaces(currentSession, update.projectId)
Message.success('项目设置已更新')
}
function handleSelectSearchResult(result: SearchResultDTO) {
const owningWorkspace = workspaces.find((workspace) => workspace.project.id === result.projectId)
if (!owningWorkspace) {
Message.warning('该搜索结果所在项目当前不可访问')
return
}
const target = searchResultTarget(result.type)
if (!target) {
Message.warning('该搜索结果暂不支持导航')
return
}
setActiveProjectID(owningWorkspace.project.id)
setActiveView('project')
setActiveChannel(target.channel)
setActiveTaskID(target.openTask ? result.id : null)
if (result.type === 'note') setSelectedItem(result.title)
}
return (
@@ -267,6 +283,13 @@ function App() {
onUpdateWorkspaceTask={handleUpdateWorkspaceTask}
onUpdateProject={handleUpdateProject}
onCreateProjectTag={handleCreateProjectTag}
searchQuery={workspaceSearch.query}
searchLoading={workspaceSearch.loading}
searchSearched={workspaceSearch.searched}
searchResults={workspaceSearch.results}
onSearchQueryChange={workspaceSearch.onQueryChange}
onSearch={() => void workspaceSearch.onSearch()}
onSelectSearchResult={handleSelectSearchResult}
/>
) : (
<Spin loading />
@@ -286,18 +309,11 @@ function App() {
)
}
function projectIconLabel(icon: string, fallback: string) {
const trimmed = icon.trim()
if (!trimmed) return fallback
const chars = Array.from(trimmed)
if (chars.length <= 2) return trimmed
return chars.slice(0, 2).join('').toUpperCase()
}
function projectColor(background: string, fallback: string) {
const trimmed = background.trim()
if (trimmed.startsWith('#') || trimmed.startsWith('rgb') || trimmed.startsWith('hsl')) return trimmed
return fallback
function searchResultTarget(type: string): { channel: ChannelKey; openTask: boolean } | null {
if (type === 'project') return { channel: 'overview', openTask: false }
if (type === 'task') return { channel: 'tasks', openTask: true }
if (type === 'note') return { channel: 'notes', openTask: false }
return null
}
export default App