Skip to content

Commit ef8a90f

Browse files
committed
explain schema with ai
1 parent f6b0e0c commit ef8a90f

File tree

7 files changed

+459
-36
lines changed

7 files changed

+459
-36
lines changed

packages/web-console/src/providers/LocalStorageProvider/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ type Props = {}
3939

4040
export const DEFAULT_AI_ASSISTANT_SETTINGS = {
4141
apiKey: "",
42-
model: "claude-3-5-sonnet-latest",
42+
model: "claude-sonnet-4-0",
4343
grantSchemaAccess: true,
4444
maxTokens: 1000
4545
}

packages/web-console/src/scenes/Editor/index.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ import type { QueryKey } from "../../store/Query/types"
3737
import type { ErrorResult } from "../../utils"
3838
import { useDispatch } from "react-redux"
3939
import { actions } from "../../store"
40-
import { AIStatusProvider } from "../../providers/AIStatusProvider"
4140

4241
type Props = Readonly<{
4342
style?: CSSProperties
@@ -89,13 +88,11 @@ const Editor = ({
8988

9089
return (
9190
<EditorPaneWrapper ref={innerRef} {...rest}>
92-
<AIStatusProvider>
93-
<Tabs />
94-
{activeBuffer.isDiffBuffer && <DiffEditorComponent pendingFixRef={pendingFixRef} />}
95-
{activeBuffer.editorViewState && !activeBuffer.isDiffBuffer && <Monaco executionRefs={executionRefs} pendingFixRef={pendingFixRef} />}
96-
{activeBuffer.metricsViewState && <Metrics key={activeBuffer.id} />}
97-
{activeBuffer.editorViewState && !activeBuffer.isDiffBuffer && <Notifications onClearNotifications={handleClearNotifications} />}
98-
</AIStatusProvider>
91+
<Tabs />
92+
{activeBuffer.isDiffBuffer && <DiffEditorComponent pendingFixRef={pendingFixRef} />}
93+
{activeBuffer.editorViewState && !activeBuffer.isDiffBuffer && <Monaco executionRefs={executionRefs} pendingFixRef={pendingFixRef} />}
94+
{activeBuffer.metricsViewState && <Metrics key={activeBuffer.id} />}
95+
{activeBuffer.editorViewState && !activeBuffer.isDiffBuffer && <Notifications onClearNotifications={handleClearNotifications} />}
9996
</EditorPaneWrapper>
10097
)
10198
}

packages/web-console/src/scenes/Layout/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import "allotment/dist/style.css"
4242

4343
import { eventBus } from "../../modules/EventBus"
4444
import { EventType } from "../../modules/EventBus/types"
45+
import { AIStatusProvider } from "../../providers/AIStatusProvider"
4546

4647
const Page = styled.div`
4748
display: flex;
@@ -101,6 +102,7 @@ const Layout = () => {
101102
return (
102103
<SearchProvider>
103104
<EditorProvider>
105+
<AIStatusProvider>
104106
<TopBar />
105107
<Warnings />
106108
<Root>
@@ -124,7 +126,8 @@ const Layout = () => {
124126

125127
<SideMenu />
126128

127-
<Footer />
129+
<Footer />
130+
</AIStatusProvider>
128131
</EditorProvider>
129132
</SearchProvider>
130133
)
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
import React from "react"
2+
import {
3+
Dialog,
4+
ForwardRef,
5+
Button,
6+
Overlay,
7+
Box,
8+
Text,
9+
} from "@questdb/react-components"
10+
import styled from "styled-components"
11+
import { AutoAwesome } from "@styled-icons/material"
12+
import { Check } from "@styled-icons/boxicons-regular"
13+
import { TableSchemaExplanation } from "../../../utils/claude"
14+
15+
const StyledDialogContent = styled(Dialog.Content)`
16+
max-width: 800px;
17+
max-height: 80vh;
18+
`
19+
20+
const StyledDescription = styled(Dialog.Description)`
21+
display: flex;
22+
flex-direction: column;
23+
gap: 2rem;
24+
overflow-y: auto;
25+
padding-right: 1rem;
26+
max-height: 60vh;
27+
28+
&::-webkit-scrollbar {
29+
width: 8px;
30+
}
31+
32+
&::-webkit-scrollbar-track {
33+
background: ${({ theme }) => theme.color.gray1};
34+
border-radius: 4px;
35+
}
36+
37+
&::-webkit-scrollbar-thumb {
38+
background: ${({ theme }) => theme.color.gray2};
39+
border-radius: 4px;
40+
}
41+
42+
&::-webkit-scrollbar-thumb:hover {
43+
background: ${({ theme }) => theme.color.gray2};
44+
}
45+
`
46+
47+
const ExplanationText = styled(Text)`
48+
white-space: pre-wrap;
49+
line-height: 1.6;
50+
font-size: 1.4rem;
51+
`
52+
53+
const TitleIcon = styled(Box)`
54+
display: flex;
55+
align-items: center;
56+
gap: 1rem;
57+
`
58+
59+
const AIBadge = styled(Box)`
60+
display: inline-flex;
61+
align-items: center;
62+
gap: 0.5rem;
63+
background: ${({ theme }) => theme.color.purple}20;
64+
color: ${({ theme }) => theme.color.purple};
65+
padding: 0.2rem 0.8rem;
66+
border-radius: 0.4rem;
67+
font-size: 1.2rem;
68+
`
69+
70+
const Section = styled.div`
71+
display: flex;
72+
flex-direction: column;
73+
gap: 1rem;
74+
`
75+
76+
const SectionTitle = styled(Text)`
77+
font-weight: 600;
78+
font-size: 1.6rem;
79+
color: ${({ theme }) => theme.color.foreground};
80+
margin-bottom: 0.5rem;
81+
`
82+
83+
const ColumnsTable = styled.table`
84+
width: 100%;
85+
border-collapse: collapse;
86+
font-size: 1.3rem;
87+
88+
th {
89+
text-align: left;
90+
padding: 0.8rem;
91+
color: ${({ theme }) => theme.color.foreground};
92+
font-weight: 700;
93+
border-bottom: 1px solid ${({ theme }) => theme.color.gray2};
94+
}
95+
96+
td {
97+
padding: 0.8rem;
98+
border-bottom: 1px solid ${({ theme }) => theme.color.gray1};
99+
&:nth-child(1), &:nth-child(2) {
100+
padding-right: 1.5rem;
101+
}
102+
}
103+
104+
tbody tr:hover {
105+
background: ${({ theme }) => theme.color.gray1}20;
106+
}
107+
`
108+
109+
const DataTypeCell = styled.td`
110+
font-family: ${({ theme }) => theme.fontMonospace};
111+
color: ${({ theme }) => theme.color.cyan};
112+
`
113+
114+
const StorageList = styled.ul`
115+
margin: 0;
116+
padding: 0;
117+
list-style: none;
118+
`
119+
120+
const StorageDetail = styled.li`
121+
display: flex;
122+
align-items: center;
123+
gap: 0.8rem;
124+
color: ${({ theme }) => theme.color.foreground};
125+
font-size: 1.4rem;
126+
margin-bottom: 0.8rem;
127+
128+
&:last-child {
129+
margin-bottom: 0;
130+
}
131+
`
132+
133+
const CheckIcon = styled(Check)`
134+
color: ${({ theme }) => theme.color.cyan};
135+
flex-shrink: 0;
136+
margin-top: 0.2rem;
137+
`
138+
139+
type Props = {
140+
open: boolean
141+
onOpenChange: (open: boolean) => void
142+
tableName: string
143+
explanation: TableSchemaExplanation | null
144+
}
145+
146+
export const SchemaExplanationDialog = ({
147+
open,
148+
onOpenChange,
149+
tableName,
150+
explanation,
151+
}: Props) => {
152+
return (
153+
<Dialog.Root open={open} onOpenChange={onOpenChange}>
154+
<Dialog.Portal>
155+
<ForwardRef>
156+
<Overlay primitive={Dialog.Overlay} />
157+
</ForwardRef>
158+
159+
<StyledDialogContent
160+
data-hook="schema-explanation-dialog"
161+
onClick={(e: React.MouseEvent<HTMLDivElement>) => {
162+
e.stopPropagation()
163+
}}
164+
onEscapeKeyDown={() => onOpenChange(false)}
165+
onPointerDownOutside={() => onOpenChange(false)}
166+
>
167+
<Dialog.Title>
168+
<TitleIcon>
169+
{tableName}
170+
<AIBadge>
171+
<AutoAwesome size={14} />
172+
AI Explanation
173+
</AIBadge>
174+
</TitleIcon>
175+
</Dialog.Title>
176+
177+
<StyledDescription>
178+
{explanation && (
179+
<>
180+
{explanation.explanation && (
181+
<Section>
182+
<SectionTitle>Overview</SectionTitle>
183+
<ExplanationText color="foreground">
184+
{explanation.explanation}
185+
</ExplanationText>
186+
</Section>
187+
)}
188+
189+
{explanation.columns && explanation.columns.length > 0 && (
190+
<Section>
191+
<SectionTitle>Columns</SectionTitle>
192+
<ColumnsTable>
193+
<thead>
194+
<tr>
195+
<th>Column Name</th>
196+
<th>Data Type</th>
197+
<th>Description</th>
198+
</tr>
199+
</thead>
200+
<tbody>
201+
{explanation.columns.map((column, index) => (
202+
<tr key={index}>
203+
<td>{column.name}</td>
204+
<DataTypeCell>{column.data_type}</DataTypeCell>
205+
<td>{column.description}</td>
206+
</tr>
207+
))}
208+
</tbody>
209+
</ColumnsTable>
210+
</Section>
211+
)}
212+
213+
{explanation.storage_details && explanation.storage_details.length > 0 && (
214+
<Section>
215+
<SectionTitle>Storage Details</SectionTitle>
216+
<StorageList>
217+
{explanation.storage_details.map((detail, index) => (
218+
<StorageDetail key={index}>
219+
<CheckIcon size={16} />
220+
<span>{detail}</span>
221+
</StorageDetail>
222+
))}
223+
</StorageList>
224+
</Section>
225+
)}
226+
</>
227+
)}
228+
</StyledDescription>
229+
230+
<Dialog.ActionButtons>
231+
<Dialog.Close asChild>
232+
<Button
233+
skin="secondary"
234+
data-hook="schema-explanation-dialog-close"
235+
onClick={() => onOpenChange(false)}
236+
>
237+
Close
238+
</Button>
239+
</Dialog.Close>
240+
</Dialog.ActionButtons>
241+
</StyledDialogContent>
242+
</Dialog.Portal>
243+
</Dialog.Root>
244+
)
245+
}

0 commit comments

Comments
 (0)