onboarding

This commit is contained in:
Saxon Fletcher committed 2025-09-17 20:28:32 +10:00
1 parent 32a634be11
commit dc4deabcbb
2 files changed
+142 -128

No files matched your search

@@ -312,7 +312,6 @@ export const AIAssistant = ({ className }: AIAssistantProps) => {
)
const hasMessages = chatMessages.length > 0
const isShowingOnboarding = !hasMessages && isApiKeySet
const sendMessageToAssistant = (finalContent: string) => {
if (editingMessageId) {
@@ -404,18 +403,17 @@ export const AIAssistant = ({ className }: AIAssistantProps) => {
]}
>
<div className={cn('flex flex-col h-full', className)}>
<Conversation className={cn('flex-1')}>
<AIAssistantHeader
isChatLoading={isChatLoading}
onNewChat={snap.newChat}
onCloseAssistant={snap.closeAssistant}
showMetadataWarning={showMetadataWarning}
updatedOptInSinceMCP={updatedOptInSinceMCP}
isHipaaProjectDisallowed={isHipaaProjectDisallowed as boolean}
aiOptInLevel={aiOptInLevel}
/>
{hasMessages ? (
<AIAssistantHeader
isChatLoading={isChatLoading}
onNewChat={snap.newChat}
onCloseAssistant={snap.closeAssistant}
showMetadataWarning={showMetadataWarning}
updatedOptInSinceMCP={updatedOptInSinceMCP}
isHipaaProjectDisallowed={isHipaaProjectDisallowed as boolean}
aiOptInLevel={aiOptInLevel}
/>
{hasMessages ? (
<Conversation className={cn('flex-1')}>
<ConversationContent className="w-full px-7 py-8 mb-10">
{renderedMessages}
{error && (
@@ -459,21 +457,20 @@ export const AIAssistant = ({ className }: AIAssistantProps) => {
/>
)}
</ConversationContent>
) : (
<AIOnboarding
sqlSnippets={snap.sqlSnippets as SqlSnippet[] | undefined}
suggestions={
snap.suggestions as
| { title?: string; prompts?: { label: string; description: string }[] }
| undefined
}
onValueChange={(val) => setValue(val)}
onFocusInput={() => inputRef.current?.focus()}
/>
)}
<ConversationScrollButton />
</Conversation>
<ConversationScrollButton />
</Conversation>
) : (
<AIOnboarding
sqlSnippets={snap.sqlSnippets as SqlSnippet[] | undefined}
suggestions={
snap.suggestions as
| { title?: string; prompts?: { label: string; description: string }[] }
| undefined
}
onValueChange={(val) => setValue(val)}
onFocusInput={() => inputRef.current?.focus()}
/>
)}
<AnimatePresence>
{editingMessageId && (
@@ -1,7 +1,7 @@
import { motion } from 'framer-motion'
import { BarChart, FileText, Shield } from 'lucide-react'
import { Button } from 'ui'
import { Button, Skeleton } from 'ui'
import { useParams } from 'common'
import { LINTER_LEVELS } from 'components/interfaces/Linter/Linter.constants'
import { createLintSummaryPrompt, lintInfoMap } from 'components/interfaces/Linter/Linter.utils'
@@ -36,7 +36,12 @@ export const AIOnboarding = ({
: defaultPrompts
const { ref: projectRef } = useParams()
const { data: lints } = useProjectLintsQuery({ projectRef })
const {
data: lints,
isLoading: isLoadingLints,
isFetching: isFetchingLints,
} = useProjectLintsQuery({ projectRef })
const isLintsLoading = isLoadingLints || isFetchingLints
const errorLints: Lint[] = (lints?.filter((lint) => lint.level === LINTER_LEVELS.ERROR) ??
[]) as Lint[]
@@ -44,114 +49,126 @@ export const AIOnboarding = ({
const performanceErrorLints = errorLints.filter((lint) => lint.categories?.[0] !== 'SECURITY')
return (
<div className="w-full space-y-6 flex-1 overflow-auto py-8 px-4 justify-end flex flex-col gap-0">
<h2 className="heading-section text-foreground mx-4">How can I assist you?</h2>
{suggestions?.prompts?.length ? (
<div>
<h3 className="heading-meta text-foreground-light mb-3 mx-4">Suggestions</h3>
{prompts.map((item, index) => (
<motion.div
key={index}
initial={{ y: 5, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.3, delay: index * 0.05 }}
>
<Button
size="small"
type="text"
className="w-full justify-start border-b hover:border-b-0 hover:rounded-md rounded-none"
icon={<FileText strokeWidth={1.5} size={14} className="text-foreground-light" />}
onClick={() => {
onValueChange(item.prompt)
onFocusInput?.()
}}
>
{item.title}
</Button>
</motion.div>
))}
</div>
) : (
<>
{performanceErrorLints.length > 0 && (
<div className="mb-4">
<h3 className="heading-meta text-foreground-light mb-3 mx-4">Improve Performance</h3>
{performanceErrorLints.map((lint, index) => {
return (
<motion.div
key={lint.cache_key ?? index}
initial={{ y: 5, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.3, delay: index * 0.03 }}
>
<Button
size="small"
type="text"
className="w-full justify-start"
icon={
<BarChart strokeWidth={1.5} size={14} className="text-foreground-light" />
}
onClick={() => {
onValueChange(createLintSummaryPrompt(lint))
onFocusInput?.()
}}
>
{lint.detail ? lint.detail.replace('\\`', '') : lint.title}
</Button>
</motion.div>
)
})}
</div>
)}
{securityErrorLints.length > 0 && (
<div className="mb-4">
<h3 className="heading-meta text-foreground-light mb-3 mx-4">Improve Security</h3>
{securityErrorLints.map((lint, index) => {
return (
<div className="flex-1 overflow-y-auto">
<div className="w-full flex-1 max-h-full min-h-full px-4 flex flex-col gap-0">
<div className="mt-auto w-full space-y-6 py-8 ">
<h2 className="heading-section text-foreground mx-4">How can I assist you?</h2>
{suggestions?.prompts?.length ? (
<div>
<h3 className="heading-meta text-foreground-light mb-3 mx-4">Suggestions</h3>
{prompts.map((item, index) => (
<motion.div
key={index}
initial={{ y: 5, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.3, delay: index * 0.05 }}
>
<Button
size="small"
type="text"
className="w-full justify-start"
icon={<Shield strokeWidth={1.5} size={14} className="text-warning" />}
className="w-full justify-start border-b hover:border-b-0 hover:rounded-md rounded-none"
icon={
<FileText strokeWidth={1.5} size={14} className="text-foreground-light" />
}
onClick={() => {
onValueChange(createLintSummaryPrompt(lint))
onValueChange(item.prompt)
onFocusInput?.()
}}
>
{lint.detail ? lint.detail.replace(/`/g, '') : lint.title}
{item.title}
</Button>
)
})}
</motion.div>
))}
</div>
)}
) : (
<>
{isLintsLoading ? (
<div className="px-4 flex flex-col gap-2">
{Array.from({ length: 6 }).map((_, index) => (
<Skeleton className="h-4 w-full" />
))}
</div>
) : (
<>
{performanceErrorLints.length > 0 && (
<div className="mb-4">
<h3 className="heading-meta text-foreground-light mb-3 mx-4">
Improve Performance
</h3>
{performanceErrorLints.map((lint, index) => {
return (
<Button
size="small"
type="text"
className="w-full justify-start"
icon={
<BarChart
strokeWidth={1.5}
size={14}
className="text-foreground-light"
/>
}
onClick={() => {
onValueChange(createLintSummaryPrompt(lint))
onFocusInput?.()
}}
>
{lint.detail ? lint.detail.replace('\\`', '') : lint.title}
</Button>
)
})}
</div>
)}
<div>
<h3 className="heading-meta text-foreground-light mb-3 mx-4">Ideas</h3>
{prompts.map((item, index) => (
<motion.div
key={index}
initial={{ y: 5, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.3, delay: index * 0.05 }}
>
<Button
size="small"
type="text"
className="w-full justify-start"
icon={<FileText strokeWidth={1.5} size={14} className="text-foreground-light" />}
onClick={() => {
onValueChange(item.prompt)
onFocusInput?.()
}}
>
{item.title}
</Button>
</motion.div>
))}
</div>
</>
)}
{securityErrorLints.length > 0 && (
<div className="mb-4">
<h3 className="heading-meta text-foreground-light mb-3 mx-4">
Improve Security
</h3>
{securityErrorLints.map((lint, index) => {
return (
<Button
size="small"
type="text"
className="w-full justify-start"
icon={<Shield strokeWidth={1.5} size={14} className="text-warning" />}
onClick={() => {
onValueChange(createLintSummaryPrompt(lint))
onFocusInput?.()
}}
>
{lint.detail ? lint.detail.replace(/`/g, '') : lint.title}
</Button>
)
})}
</div>
)}
<div>
<h3 className="heading-meta text-foreground-light mb-3 mx-4">Ideas</h3>
{prompts.map((item, index) => (
<Button
size="small"
type="text"
className="w-full justify-start"
icon={
<FileText strokeWidth={1.5} size={14} className="text-foreground-light" />
}
onClick={() => {
onValueChange(item.prompt)
onFocusInput?.()
}}
>
{item.title}
</Button>
))}
</div>
</>
)}
</>
)}
</div>
</div>
</div>
)
}