All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 7s
- Add brainstorm feature with collaborative canvas, AI idea generation, live cursors, playback, and export - Add PDF upload/extraction/ingestion pipeline with pgvector document search (RAG) - Add document Q&A overlay with streaming chat and PDF preview - Add note attachments UI with status polling, grid layout, and auto-scroll - Add task extraction AI tool and agent executor improvements - Fix NoteEmbedding missing updatedAt column, re-index 66 notes with 1536-dim embeddings - Fix brainstorm 'Create Note' button: add success toast and redirect to created note - Fix memory echo notification infinite polling - Fix chat route to always include document_search tool - Add brainstorm i18n keys across all 14 locales - Add socket server for real-time brainstorm collaboration - Add hierarchical notebook selector and organize notebook dialog improvements - Add sidebar brainstorm section with session management - Update prisma schema with brainstorm tables, attachments, and document chunks
317 lines
9.8 KiB
TypeScript
317 lines
9.8 KiB
TypeScript
'use client'
|
|
|
|
import { useQueryClient, useMutation, useQuery } from '@tanstack/react-query'
|
|
import { queryKeys } from '@/lib/query-keys'
|
|
import type { BrainstormSession, BrainstormSessionListItem } from '@/types/brainstorm'
|
|
|
|
export interface CreateBrainstormResult {
|
|
session: BrainstormSession
|
|
contextSummary?: {
|
|
support: number
|
|
tension: number
|
|
extension: number
|
|
}
|
|
}
|
|
|
|
export function useBrainstormSessions() {
|
|
return useQuery({
|
|
queryKey: queryKeys.brainstormSessions(),
|
|
queryFn: async (): Promise<BrainstormSessionListItem[]> => {
|
|
const res = await fetch('/api/brainstorm', { credentials: 'include' })
|
|
const data = await res.json()
|
|
return data.data || []
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useSharedBrainstormSessions() {
|
|
return useQuery({
|
|
queryKey: queryKeys.brainstormSharedSessions(),
|
|
queryFn: async (): Promise<BrainstormSessionListItem[]> => {
|
|
const res = await fetch('/api/brainstorm/shared', { credentials: 'include' })
|
|
const data = await res.json()
|
|
return data.data || []
|
|
},
|
|
})
|
|
}
|
|
|
|
export interface BrainstormSessionMeta {
|
|
role: 'owner' | 'editor' | 'viewer' | 'guest' | 'none'
|
|
canEdit: boolean
|
|
}
|
|
|
|
export interface BrainstormSessionResult {
|
|
session: BrainstormSession
|
|
meta?: BrainstormSessionMeta
|
|
}
|
|
|
|
export function useBrainstormSession(sessionId: string | null) {
|
|
return useQuery({
|
|
queryKey: queryKeys.brainstormSession(sessionId || ''),
|
|
queryFn: async (): Promise<BrainstormSessionResult | null> => {
|
|
if (!sessionId) return null
|
|
const res = await fetch(`/api/brainstorm/${sessionId}`, { credentials: 'include' })
|
|
const data = await res.json()
|
|
if (!data.data) return null
|
|
return {
|
|
session: data.data,
|
|
meta: data._meta || undefined,
|
|
}
|
|
},
|
|
enabled: !!sessionId,
|
|
})
|
|
}
|
|
|
|
export function useCreateBrainstorm() {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: async (input: {
|
|
seedIdea: string
|
|
sourceNoteId?: string
|
|
contextNoteIds?: string[]
|
|
locale?: string
|
|
}): Promise<CreateBrainstormResult> => {
|
|
const res = await fetch('/api/brainstorm', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
credentials: 'include',
|
|
body: JSON.stringify(input),
|
|
})
|
|
const data = await res.json()
|
|
if (!data.success) throw new Error(data.error || 'Failed to create brainstorm')
|
|
return { session: data.data, contextSummary: data.contextSummary }
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSessions() })
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useExpandIdea(sessionId: string) {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: async (input: { ideaId: string; locale?: string }): Promise<BrainstormSession> => {
|
|
const res = await fetch(`/api/brainstorm/${sessionId}/expand`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
credentials: 'include',
|
|
body: JSON.stringify(input),
|
|
})
|
|
const data = await res.json()
|
|
if (!data.success) throw new Error(data.error || 'Failed to expand idea')
|
|
return data.data
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSession(sessionId) })
|
|
queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSessions() })
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useDismissIdea(sessionId: string) {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: async (ideaId: string) => {
|
|
const res = await fetch(`/api/brainstorm/${sessionId}/dismiss`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
credentials: 'include',
|
|
body: JSON.stringify({ ideaId }),
|
|
})
|
|
const data = await res.json()
|
|
if (!data.success) throw new Error(data.error || 'Failed to dismiss idea')
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSession(sessionId) })
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useConvertIdea(sessionId: string) {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: async (ideaId: string) => {
|
|
const res = await fetch(`/api/brainstorm/${sessionId}/convert`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
credentials: 'include',
|
|
body: JSON.stringify({ ideaId }),
|
|
})
|
|
const data = await res.json()
|
|
if (!data.success) throw new Error(data.error || 'Failed to convert idea')
|
|
return data.data
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSession(sessionId) })
|
|
queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSessions() })
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useExportBrainstorm(sessionId: string) {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: async () => {
|
|
const res = await fetch(`/api/brainstorm/${sessionId}/export`, {
|
|
method: 'POST',
|
|
credentials: 'include',
|
|
})
|
|
const data = await res.json()
|
|
if (!data.success) throw new Error(data.error || 'Failed to export brainstorm')
|
|
return data.data
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSession(sessionId) })
|
|
queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSessions() })
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useFinalizeBrainstorm(sessionId: string) {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: async () => {
|
|
const res = await fetch(`/api/brainstorm/${sessionId}/finalize`, {
|
|
method: 'POST',
|
|
credentials: 'include',
|
|
})
|
|
const data = await res.json()
|
|
if (!data.success) throw new Error(data.error || 'Failed to finalize brainstorm')
|
|
return data.impact
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSessions() })
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useDeleteBrainstorm() {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: async (sessionId: string) => {
|
|
const res = await fetch(`/api/brainstorm/${sessionId}`, {
|
|
method: 'DELETE',
|
|
credentials: 'include',
|
|
})
|
|
const data = await res.json()
|
|
if (!data.success) throw new Error(data.error || 'Failed to delete brainstorm')
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSessions() })
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useInviteParticipant(sessionId: string) {
|
|
return useMutation({
|
|
mutationFn: async (input: { role: 'editor' | 'viewer'; expiresInHours?: number; email?: string }) => {
|
|
const res = await fetch(`/api/brainstorm/${sessionId}/invite`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
credentials: 'include',
|
|
body: JSON.stringify(input),
|
|
})
|
|
const data = await res.json()
|
|
if (!data.success) throw new Error(data.error || 'Failed to create invite')
|
|
return data
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useJoinBrainstorm() {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: async (token: string) => {
|
|
const res = await fetch(`/api/brainstorm/join`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
credentials: 'include',
|
|
body: JSON.stringify({ token }),
|
|
})
|
|
const data = await res.json()
|
|
if (!data.success) throw new Error(data.error || 'Failed to join')
|
|
return data
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSessions() })
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useAddManualIdea(sessionId: string) {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: async (input: { title: string; description?: string; parentIdeaId?: string; locale?: string }) => {
|
|
const res = await fetch(`/api/brainstorm/${sessionId}/manual-idea`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
credentials: 'include',
|
|
body: JSON.stringify(input),
|
|
})
|
|
const data = await res.json()
|
|
if (!data.success) throw new Error(data.error || 'Failed to add idea')
|
|
return data.data
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSession(sessionId) })
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useBrainstormActivity(sessionId: string | null) {
|
|
return useQuery({
|
|
queryKey: ['brainstorm', 'activity', sessionId],
|
|
queryFn: async () => {
|
|
if (!sessionId) return []
|
|
const res = await fetch(`/api/brainstorm/${sessionId}/activity`, { credentials: 'include' })
|
|
const data = await res.json()
|
|
return data.data || []
|
|
},
|
|
enabled: !!sessionId,
|
|
refetchInterval: 10000,
|
|
})
|
|
}
|
|
|
|
export function useUpdateBrainstormSettings(sessionId: string) {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: async (input: { isPublic?: boolean; guestCanEdit?: boolean }) => {
|
|
const res = await fetch(`/api/brainstorm/${sessionId}`, {
|
|
method: 'PATCH',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
credentials: 'include',
|
|
body: JSON.stringify(input),
|
|
})
|
|
const data = await res.json()
|
|
if (!data.success) throw new Error(data.error || 'Failed to update settings')
|
|
return data.data
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSession(sessionId) })
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useBrainstormSnapshots(sessionId: string | null) {
|
|
return useQuery({
|
|
queryKey: ['brainstorm', 'snapshots', sessionId],
|
|
queryFn: async () => {
|
|
if (!sessionId) return []
|
|
const res = await fetch(`/api/brainstorm/${sessionId}/snapshots`, { credentials: 'include' })
|
|
const data = await res.json()
|
|
return data.data || []
|
|
},
|
|
enabled: !!sessionId,
|
|
})
|
|
}
|