Skip to content
This repository was archived by the owner on Apr 28, 2024. It is now read-only.

Commit 0a7f2a7

Browse files
authored
Merge pull request #132 from slashbaseide/develop
Release v0.10.0
2 parents 066c0ae + a7c8359 commit 0a7f2a7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1106
-123
lines changed

frontend/desktop/package.json.md5

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
371e648509f99cc686955a6284995fb5
1+
a24ece3a3d86e13594977247e3352385

frontend/desktop/src/components/dbfragments/console.tsx

+16-4
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ const DBConsoleFragment = ({ }: DBConsolePropType) => {
2121
const output = useAppSelector(selectBlocks)
2222
const [input, setInput] = useState("")
2323
const [nfocus, setFocus] = useState<number>(0)
24-
24+
const commands = output.filter( e => e.cmd === true)
25+
const [pointer, setPointer] = useState<number>(commands.length-1)
2526
useEffect(() => {
2627
dispatch(initConsole(dbConnection!.id))
2728
}, [dbConnection])
@@ -49,7 +50,7 @@ const DBConsoleFragment = ({ }: DBConsolePropType) => {
4950
{output.map((block, idx) => {
5051
return <OutputBlock block={block} key={idx} />
5152
})}
52-
<PromptInputWithRef onChange={setInput} isActive={currentTab.isActive} nfocus={nfocus} confirmInput={confirmInput} />
53+
<PromptInputWithRef onChange={setInput} isActive={currentTab.isActive} nfocus={nfocus} confirmInput={confirmInput} commands={commands} pointer={pointer} setPointer={setPointer} />
5354
<span ref={consoleEndRef}></span>
5455
</div>
5556
}
@@ -63,10 +64,8 @@ const OutputBlock = ({ block }: any) => {
6364

6465

6566
const PromptInputWithRef = (props: any) => {
66-
6767
const defaultValue = useRef("")
6868
const inputRef = useRef<HTMLParagraphElement>(null)
69-
7069
useEffect(() => {
7170
if (props.isActive) {
7271
inputRef.current?.focus()
@@ -79,13 +78,26 @@ const PromptInputWithRef = (props: any) => {
7978
}
8079
}
8180

81+
const setInputRef = ( cmd : string) => {
82+
if(inputRef.current !== null){
83+
inputRef.current.textContent = cmd;
84+
}
85+
}
8286
const handleKeyUp = (event: React.KeyboardEvent) => {
8387
if (props.confirmInput && event.key.toLocaleLowerCase() === 'enter') {
8488
props.confirmInput()
8589
if (inputRef.current) {
8690
inputRef.current.innerText = ""
8791
}
8892
}
93+
if ( event.key.toLocaleLowerCase() === 'arrowup') {
94+
props.setPointer( () => ((props.pointer + props.commands.length -1 ) % props.commands.length))
95+
setInputRef(props.commands.at(props.pointer)?.text)
96+
}
97+
if ( event.key.toLocaleLowerCase() === 'arrowdown'){
98+
props.setPointer( () => ((props.pointer + 1 ) % props.commands.length))
99+
setInputRef(props.commands.at(props.pointer)?.text)
100+
}
89101
}
90102

91103
return <p

frontend/desktop/src/components/dbfragments/gensql.module.scss

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import React, { useContext, useState } from "react"
2+
import { Tab } from "../../data/models"
3+
import { selectDBConnection } from "../../redux/dbConnectionSlice"
4+
import { useAppDispatch, useAppSelector } from "../../redux/hooks"
5+
import TabContext from "../layouts/tabcontext"
6+
import styles from './gensql.module.scss'
7+
import eventService from "../../events/eventService"
8+
import { duotoneLight } from '@uiw/codemirror-theme-duotone'
9+
import { toast } from "react-hot-toast"
10+
import ReactCodeMirror from "@uiw/react-codemirror"
11+
import { sql } from '@codemirror/lang-sql'
12+
import { TabType } from "../../data/defaults"
13+
import { createTab } from "../../redux/tabsSlice"
14+
15+
type DBGenSQLPropType = {
16+
}
17+
18+
const DBGenSQLFragment = ({ }: DBGenSQLPropType) => {
19+
20+
const dispatch = useAppDispatch()
21+
22+
const currentTab: Tab = useContext(TabContext)!
23+
24+
const [inputValue, setInputValue] = useState<string>('')
25+
const [generating, setGenerating] = useState<boolean>(false)
26+
const [outputValue, setOutputValue] = useState<string | undefined>()
27+
28+
const dbConnection = useAppSelector(selectDBConnection)
29+
30+
const onChange = React.useCallback((value: any) => {
31+
setOutputValue(value)
32+
}, [])
33+
34+
const runGenerateSQL = async () => {
35+
if (generating) {
36+
return
37+
}
38+
setGenerating(true)
39+
const result = await eventService.runGenerateSQL(dbConnection!.id, inputValue)
40+
if (result.success)
41+
setOutputValue(result.data)
42+
else
43+
toast.error(result.error!)
44+
setGenerating(false)
45+
}
46+
47+
const openInQueryEditor = () => {
48+
dispatch(createTab({ dbConnId: dbConnection!.id, tabType: TabType.QUERY, metadata: { queryId: "new", query: outputValue } }))
49+
}
50+
51+
const copyToClipboard = () => {
52+
navigator.clipboard.writeText(outputValue!)
53+
toast.success("copied")
54+
}
55+
56+
return <div className={styles.console + " " + (currentTab.isActive ? "db-tab-active" : "db-tab")}>
57+
<div className={"control" + (generating ? " is-loading" : "")}>
58+
<textarea
59+
value={inputValue}
60+
className="textarea"
61+
placeholder="Enter prompt to generate SQL"
62+
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => { setInputValue(e.target.value) }}
63+
/>
64+
</div>
65+
<br />
66+
<div className="control">
67+
{!generating && <button className={"button" + (outputValue === undefined ? " is-primary" : "")} onClick={runGenerateSQL}>
68+
<span className="icon is-small">
69+
<i className="fas fa-play-circle" aria-hidden="true"></i>
70+
</span>&nbsp;&nbsp;
71+
Generate
72+
</button>}
73+
{generating && <button className="button is-primary is-loading">Running</button>}
74+
</div>
75+
{outputValue !== undefined && <>
76+
<br /><br />
77+
<ReactCodeMirror
78+
value={outputValue}
79+
extensions={[sql()]}
80+
theme={duotoneLight}
81+
height={"auto"}
82+
minHeight="80px"
83+
placeholder={"Generated SQL"}
84+
basicSetup={{
85+
autocompletion: false,
86+
highlightActiveLine: false,
87+
}}
88+
onChange={onChange}
89+
/>
90+
<br />
91+
<div className="buttons">
92+
<button className="button is-primary" onClick={openInQueryEditor}>
93+
<span className="icon is-small">
94+
<i className="fas fa-edit" aria-hidden="true"></i>
95+
</span>&nbsp;&nbsp;
96+
Open in Query Editor
97+
</button>
98+
<button className="button" onClick={copyToClipboard}>
99+
<span className="icon is-small">
100+
<i className="fas fa-copy" aria-hidden="true"></i>
101+
</span>&nbsp;&nbsp;
102+
Copy to clipboard
103+
</button>
104+
</div>
105+
</>}
106+
</div>
107+
}
108+
109+
export default DBGenSQLFragment

frontend/desktop/src/components/dbfragments/query.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ const DBQueryFragment = () => {
121121
mName={''}
122122
onFilterChanged={() => { }}
123123
onSortChanged={() => { }}
124-
onRefresh={()=>{}}
124+
onRefresh={() => { }}
125125
isInteractive={false} />
126126
}
127127
{dbConnection!.type === DBConnType.MONGO &&
@@ -131,7 +131,7 @@ const DBQueryFragment = () => {
131131
mName={''}
132132
onFilterChanged={() => { }}
133133
onSortChanged={() => { }}
134-
onRefresh={()=>{}}
134+
onRefresh={() => { }}
135135
isInteractive={false} />
136136
}
137137
</React.Fragment>

frontend/desktop/src/components/layouts/sidebar.tsx

+19
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ const Sidebar = () => {
4242
dispatch(createTab({ dbConnId: dbConnection!.id, tabType: TabType.CONSOLE, metadata: {} }))
4343
}
4444

45+
const openGenerateSQLTab = () => {
46+
dispatch(createTab({ dbConnId: dbConnection!.id, tabType: TabType.GENSQL, metadata: {} }))
47+
}
48+
4549
return (
4650
<aside className={"menu " + styles.sidebar}>
4751
<div className={styles.spacebox}>
@@ -99,6 +103,14 @@ const Sidebar = () => {
99103
&nbsp;Console
100104
</a>
101105
</li>
106+
<li>
107+
<a onClick={() => openGenerateSQLTab()}>
108+
<span className="icon">
109+
<i className="fas fa-magic"></i>
110+
</span>
111+
&nbsp;Generate SQL
112+
</a>
113+
</li>
102114
</ul>
103115
</React.Fragment>
104116
}
@@ -115,6 +127,13 @@ const Sidebar = () => {
115127
General
116128
</Link>
117129
</li>
130+
<li>
131+
<Link
132+
to={Constants.APP_PATHS.SETTINGS_ADVANCED.path}
133+
className={location.pathname.startsWith(Constants.APP_PATHS.SETTINGS_ADVANCED.path) ? 'is-active' : ''}>
134+
Advanced
135+
</Link>
136+
</li>
118137
</ul>
119138
<p className="menu-label">
120139
Info

frontend/desktop/src/components/layouts/tabsbar.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const TabsBar = (_: TabsBarPropType) => {
4040
{t.type === TabType.BLANK && "New Tab"}
4141
{t.type === TabType.HISTORY && "History"}
4242
{t.type === TabType.CONSOLE && "Console"}
43+
{t.type === TabType.GENSQL && "Generate SQL"}
4344
{t.type === TabType.DATA && `${t.metadata.schema === '' ? t.metadata.name : `${t.metadata.schema}.${t.metadata.name}`}`}
4445
{t.type === TabType.MODEL && `${t.metadata.schema === '' ? t.metadata.name : `${t.metadata.schema}.${t.metadata.name}`}`}
4546
{t.type === TabType.QUERY && `${t.metadata.queryName ? t.metadata.queryName : "New Query"}`}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React, { FunctionComponent, useEffect, useState } from 'react'
2+
import Constants from '../../constants'
3+
import eventService from '../../events/eventService'
4+
5+
const AdvancedSettings: FunctionComponent<{}> = () => {
6+
7+
8+
const [openAIKey, setOpenAIKey] = useState<string>("")
9+
10+
useEffect(() => {
11+
(async () => {
12+
let result = await eventService.getSingleSetting(Constants.SETTING_KEYS.OPENAI_KEY)
13+
setOpenAIKey(result.data)
14+
})()
15+
}, [])
16+
17+
const updateOpenAIKey = async () => {
18+
const result = await eventService.updateSingleSetting(Constants.SETTING_KEYS.OPENAI_KEY, openAIKey)
19+
if (result.success)
20+
setOpenAIKey(openAIKey)
21+
}
22+
23+
return (
24+
<React.Fragment>
25+
<h1>Advanced Settings</h1>
26+
<br />
27+
<h2>OpenAI Key</h2>
28+
<p>Update OpenAI API key to enable Generate SQL tool.</p>
29+
<div className="buttons has-addons">
30+
<div className="field has-addons">
31+
<p className="control is-expanded">
32+
<input
33+
className="input"
34+
type="text"
35+
value={openAIKey}
36+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => { setOpenAIKey(e.target.value) }}
37+
placeholder="Enter API key" />
38+
</p>
39+
<p className="control">
40+
<a className="button" onClick={updateOpenAIKey}>
41+
<i className="fas fa-check" />
42+
</a>
43+
</p>
44+
</div>
45+
</div>
46+
<br />
47+
</React.Fragment>
48+
)
49+
}
50+
51+
export default AdvancedSettings

frontend/desktop/src/constants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const Constants: ConstantsType = {
6363
APP_ID: "APP_ID",
6464
TELEMETRY_ENABLED: "TELEMETRY_ENABLED",
6565
LOGS_EXPIRE: "LOGS_EXPIRE",
66+
OPENAI_KEY: "OPENAI_KEY",
6667
},
6768
}
6869

frontend/desktop/src/data/defaults.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ export enum TabType {
1717
MODEL = "MODEL",
1818
QUERY = "QUERY",
1919
HISTORY = "HISTORY",
20-
CONSOLE = "CONSOLE"
20+
CONSOLE = "CONSOLE",
21+
GENSQL = "GENSQL",
2122
}
2223

2324
export enum DBConnectionLoginType {

frontend/desktop/src/events/constants.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,11 @@ const Events: EventType = {
137137
CHECK_DBCONNECTION: {
138138
REQUEST: "event:check:dbconnection",
139139
RESPONSE: "response:check:dbconnection:[dbid]"
140-
}
140+
},
141+
AI_GENSQL: {
142+
REQUEST: "event:ai:gensql",
143+
RESPONSE: "response:ai:gensql"
144+
},
141145
}
142146

143147
export default Events

frontend/desktop/src/events/eventService.ts

+10-3
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,9 @@ const updateSingleSetting = async function (name: string, value: string): Promis
168168
return response
169169
}
170170

171-
const createTab = async function (dbConnectionId: string, tabType: string, mSchema: string, mName: string, queryId: string): Promise<ApiResult<Tab>> {
171+
const createTab = async function (dbConnectionId: string, tabType: string, mSchema: string, mName: string, queryId: string, query: string): Promise<ApiResult<Tab>> {
172172
const response = responseEvent<ApiResult<Tab>>(Events.CREATE_TAB.RESPONSE)
173-
EventsEmit(Events.CREATE_TAB.REQUEST, Events.CREATE_TAB.RESPONSE, dbConnectionId, tabType, mSchema, mName, queryId)
173+
EventsEmit(Events.CREATE_TAB.REQUEST, Events.CREATE_TAB.RESPONSE, dbConnectionId, tabType, mSchema, mName, queryId, query)
174174
return response
175175
}
176176

@@ -204,6 +204,12 @@ const checkConnection = async function (dbConnId: string): Promise<ApiResult<und
204204
return response
205205
}
206206

207+
const runGenerateSQL = async function (dbConnId: string, text: string): Promise<ApiResult<string>> {
208+
const response = responseEvent<ApiResult<string>>(Events.AI_GENSQL.RESPONSE)
209+
EventsEmit(Events.AI_GENSQL.REQUEST, Events.AI_GENSQL.RESPONSE, dbConnId, text)
210+
return response
211+
}
212+
207213
export default {
208214
getHealthCheck,
209215
getProjects,
@@ -237,5 +243,6 @@ export default {
237243
updateTab,
238244
closeTab,
239245
runConsoleCommand,
240-
checkConnection
246+
checkConnection,
247+
runGenerateSQL
241248
}

frontend/desktop/src/events/payloads.ts

+1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ export interface AddDBConnPayload {
1616
sshPassword: string
1717
sshKeyFile: string
1818
useSSL: boolean
19+
isTest:boolean
1920
}

frontend/desktop/src/pages/db/index.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import DBShowDataFragment from '../../components/dbfragments/showdata'
1010
import DBShowModelFragment from '../../components/dbfragments/showmodel'
1111
import DBQueryFragment from '../../components/dbfragments/query'
1212
import DBConsoleFragment from '../../components/dbfragments/console'
13+
import DBGenSQLFragment from '../../components/dbfragments/gensql'
1314
import TabContext from '../../components/layouts/tabcontext'
1415
import { Tab } from '../../data/models'
1516

@@ -53,6 +54,7 @@ const DBPage: FunctionComponent<{}> = () => {
5354
{tab.type === TabType.MODEL && <DBShowModelFragment />}
5455
{tab.type === TabType.QUERY && <DBQueryFragment />}
5556
{tab.type === TabType.CONSOLE && <DBConsoleFragment />}
57+
{tab.type === TabType.GENSQL && <DBGenSQLFragment />}
5658
</TabContext.Provider>
5759
</React.Fragment>)
5860
)}

0 commit comments

Comments
 (0)