├── .env.production ├── .env.local ├── UI.png ├── public ├── favicon.ico ├── screenshot.png ├── copy.svg ├── copyDark.svg ├── twitterDark.svg ├── twitterfill.svg ├── switch.svg ├── switchDark.svg └── stars.svg ├── next.config.js ├── postcss.config.js ├── docker-compose.yml ├── .vscode ├── launch.json └── settings.json ├── components ├── LoadingDots.tsx ├── Header │ ├── Fork.tsx │ ├── Star.tsx │ └── Header.tsx ├── ThemeButton.tsx ├── GitHub.tsx ├── Toggle.tsx └── atoms │ └── Orbs.tsx ├── tsconfig.json ├── .gitignore ├── pages ├── _app.tsx ├── api │ ├── translate.ts │ └── sql-to-human.ts ├── _document.tsx └── index.tsx ├── .history ├── pages │ ├── _app_20230320135926.tsx │ └── _app_20230615142305.tsx ├── README_20230615141736.md ├── README_20230615141805.md └── README_20230615141801.md ├── tailwind.config.js ├── Dockerfile ├── LICENSE ├── src ├── translateToHuman.js └── translateToSQL.js ├── styles ├── loading-dots.module.css └── globals.css ├── hooks └── useTranslate.ts ├── package.json ├── utils └── OpenAIStream.ts └── README.md /.env.production: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY= -------------------------------------------------------------------------------- /.env.local: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY=D4mVDHuxlTQnTjY2tFne6yUdVAmDitLe -------------------------------------------------------------------------------- /UI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whoiskatrin/sql-translator/HEAD/UI.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whoiskatrin/sql-translator/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whoiskatrin/sql-translator/HEAD/public/screenshot.png -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | module.exports = { 3 | reactStrictMode: true, 4 | }; 5 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.8" 2 | services: 3 | sql-translator: 4 | container_name: sql-translator 5 | build: . 6 | env_file: 7 | - .env.production 8 | ports: 9 | - "3000:3000" 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "java", 9 | "name": "Current File", 10 | "request": "launch", 11 | "mainClass": "${file}" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "rvest.vs-code-prettier-eslint", 3 | "editor.formatOnPaste": false, // required 4 | "editor.formatOnType": false, // required 5 | "editor.formatOnSave": true, // optional 6 | "editor.formatOnSaveMode": "file", // required to format on save 7 | "files.autoSave": "onFocusChange", // optional but recommended 8 | "vs-code-prettier-eslint.prettierLast": "false" // set as "true" to run 'prettier' last not first 9 | } -------------------------------------------------------------------------------- /public/copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/copyDark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /components/LoadingDots.tsx: -------------------------------------------------------------------------------- 1 | import styles from "../styles/loading-dots.module.css"; 2 | 3 | const LoadingDots = ({ 4 | color = "#000", 5 | style = "small", 6 | }: { 7 | color: string; 8 | style?: string; 9 | }) => { 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | ); 17 | }; 18 | 19 | export default LoadingDots; 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true 17 | }, 18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "pages/api/translate.js"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | # .env 30 | # .env.development 31 | # .env*.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | next-env.d.ts 39 | 40 | # idea 41 | .idea 42 | 43 | #history 44 | .history 45 | -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import { Analytics } from "@vercel/analytics/react"; 2 | import { ThemeProvider } from "next-themes"; 3 | import type { AppProps } from "next/app"; 4 | import { Toaster } from "react-hot-toast"; 5 | import "../styles/globals.css"; 6 | 7 | function MyApp({ Component, pageProps }: AppProps) { 8 | return ( 9 | <> 10 | 15 | 16 | 17 | 18 | 19 | 20 | ); 21 | } 22 | 23 | export default MyApp; 24 | -------------------------------------------------------------------------------- /.history/pages/_app_20230320135926.tsx: -------------------------------------------------------------------------------- 1 | import { Analytics } from "@vercel/analytics/react"; 2 | import { ThemeProvider } from "next-themes"; 3 | import type { AppProps } from "next/app"; 4 | import { Toaster } from "react-hot-toast"; 5 | import "../styles/globals.css"; 6 | 7 | function MyApp({ Component, pageProps }: AppProps) { 8 | return ( 9 | <> 10 | 15 | 16 | 17 | 18 | 19 | 20 | ); 21 | } 22 | 23 | export default MyApp; 24 | -------------------------------------------------------------------------------- /.history/pages/_app_20230615142305.tsx: -------------------------------------------------------------------------------- 1 | import { Analytics } from "@vercel/analytics/react"; 2 | import { ThemeProvider } from "next-themes"; 3 | import type { AppProps } from "next/app"; 4 | import { Toaster } from "react-hot-toast"; 5 | import "../styles/globals.css"; 6 | 7 | function MyApp({ Component, pageProps }: AppProps) { 8 | return ( 9 | <> 10 | 15 | 16 | 17 | 18 | 19 | 20 | ); 21 | } 22 | 23 | export default MyApp; 24 | -------------------------------------------------------------------------------- /components/Header/Fork.tsx: -------------------------------------------------------------------------------- 1 | import { SVGProps } from "react"; 2 | 3 | const SvgComponent = (props: SVGProps) => ( 4 | 14 | ); 15 | 16 | export default SvgComponent; 17 | -------------------------------------------------------------------------------- /pages/api/translate.ts: -------------------------------------------------------------------------------- 1 | import { NextApiRequest, NextApiResponse } from "next"; 2 | import translateToSQL from "../../src/translateToSQL"; 3 | 4 | if (!process.env.OPENAI_API_KEY) { 5 | throw new Error( 6 | "OPENAI_API_KEY is not defined in .env file. Please add it there (see README.md for more details)." 7 | ); 8 | } 9 | 10 | export default async function handler( 11 | req: NextApiRequest, 12 | res: NextApiResponse 13 | ) { 14 | const { inputText, tableSchema } = req.body; 15 | try { 16 | const result = await translateToSQL( 17 | inputText, 18 | process.env.OPENAI_API_KEY, 19 | tableSchema 20 | ); 21 | // console.log(res); 22 | res.status(200).json({ outputText: result }); 23 | } catch (error) { 24 | console.error(error); 25 | res.status(500).json({ message: "Error translating to SQL" }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | future: { 4 | hoverOnlyWhenSupported: true, 5 | }, 6 | content: [ 7 | "./pages/**/*.{js,ts,jsx,tsx}", 8 | "./components/**/*.{js,ts,jsx,tsx}", 9 | "./app/**/*.{js,ts,jsx,tsx}", 10 | ], 11 | darkMode: "class", 12 | theme: { 13 | extend: { 14 | textColor: ['placeholder'], 15 | backgroundColor: { 16 | "custom-gray-bg": "#F8F8F8", 17 | "custom-gray": "#171717", 18 | "custom-dark-gray": "#1D1D1D", 19 | "frame-gray": "EEEEEE", 20 | }, 21 | borderColor: { 22 | "custom-gray": "#171717", 23 | }, 24 | placeholderColor: { 25 | 'dark': '#FFFFFF', 26 | } 27 | }, 28 | }, 29 | plugins: [require("@tailwindcss/forms"), require("@headlessui/tailwindcss")], 30 | }; 31 | -------------------------------------------------------------------------------- /components/Header/Star.tsx: -------------------------------------------------------------------------------- 1 | import { SVGProps } from "react"; 2 | 3 | const SvgComponent = (props: SVGProps) => ( 4 | 14 | ); 15 | 16 | export default SvgComponent; 17 | -------------------------------------------------------------------------------- /pages/api/sql-to-human.ts: -------------------------------------------------------------------------------- 1 | import { NextApiRequest, NextApiResponse } from "next"; 2 | import translateToHuman from "../../src/translateToHuman"; 3 | 4 | if (!process.env.OPENAI_API_KEY) { 5 | throw new Error( 6 | "OPENAI_API_KEY is not defined in .env file. Please add it there (see README.md for more details)." 7 | ); 8 | } 9 | 10 | export default async function handler( 11 | req: NextApiRequest, 12 | res: NextApiResponse 13 | ) { 14 | if (req.method !== "POST") { 15 | return res.status(405).json({ message: "Method not allowed" }); 16 | } 17 | 18 | const { inputText } = req.body; 19 | try { 20 | const outputText = await translateToHuman( 21 | inputText, 22 | process.env.OPENAI_API_KEY 23 | ); 24 | res.status(200).json({ outputText }); 25 | } catch (error) { 26 | console.error(error); 27 | res.status(500).json({ message: "Error translating to natural language" }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /components/ThemeButton.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { faMoon, faSun } from "@fortawesome/free-solid-svg-icons"; 3 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 4 | import { useTheme } from "next-themes"; 5 | 6 | export default function ThemeButton({ className }: { className?: string }) { 7 | const [mounted, setMounted] = useState(false); 8 | const { systemTheme, theme, setTheme } = useTheme(); 9 | const currentTheme = theme === "system" ? systemTheme : theme; 10 | 11 | useEffect(() => { 12 | setMounted(true) 13 | }, []) 14 | 15 | if (!mounted) { 16 | return null 17 | } 18 | 19 | return ( 20 | 32 | ); 33 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Install dependencies 2 | FROM node:alpine AS deps 3 | RUN apk add --no-cache libc6-compat 4 | WORKDIR /app 5 | COPY package.json ./ 6 | RUN yarn install --frozen-lockfile 7 | 8 | # Rebuild the source code 9 | FROM node:alpine AS builder 10 | WORKDIR /app 11 | COPY . . 12 | COPY --from=deps /app/node_modules ./node_modules 13 | RUN yarn build && yarn install --production --ignore-scripts --prefer-offline 14 | 15 | # Production image, copy all the files and run next 16 | FROM node:alpine AS runner 17 | WORKDIR /app 18 | ENV NODE_ENV production 19 | RUN addgroup -g 1001 -S nodejs 20 | RUN adduser -S nextjs -u 1001 21 | 22 | COPY --from=builder /app/next.config.js ./ 23 | COPY --from=builder /app/public ./public 24 | COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next 25 | COPY --from=builder /app/node_modules ./node_modules 26 | COPY --from=builder /app/package.json ./package.json 27 | 28 | # Execute script 29 | RUN apk add --no-cache --upgrade bash 30 | USER nextjs 31 | EXPOSE 3000 32 | ENV PORT 3000 33 | CMD ["node_modules/.bin/next", "start"] -------------------------------------------------------------------------------- /components/GitHub.tsx: -------------------------------------------------------------------------------- 1 | export default function Github({ className }: { className?: string }) { 2 | return ( 3 | 11 | 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 whoiskatrin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/translateToHuman.js: -------------------------------------------------------------------------------- 1 | import fetch from "isomorphic-unfetch"; 2 | 3 | const translateToHuman = async (query, apiKey) => { 4 | // Validate inputs 5 | if (!query || !apiKey) { 6 | throw new Error("Missing query or API key."); 7 | } 8 | 9 | const response = await fetch("https://api.openai.com/v1/completions", { 10 | method: "POST", 11 | headers: { 12 | "Content-Type": "application/json", 13 | Authorization: `Bearer ${apiKey}`, 14 | }, 15 | body: JSON.stringify({ 16 | prompt: `Translate this SQL query into natural language:\n\n"${query}"\n\nNatural language query:`, 17 | temperature: 0.5, 18 | max_tokens: 2048, 19 | n: 1, 20 | stop: "\\n", 21 | model: "text-davinci-003", 22 | frequency_penalty: 0.5, 23 | presence_penalty: 0.5, 24 | logprobs: 10, 25 | }), 26 | }); 27 | 28 | const data = await response.json(); 29 | if (!response.ok) { 30 | console.error("API Error:", response.status, data); 31 | throw new Error(data.error || "Error translating to human language."); 32 | } 33 | 34 | return data.choices[0].text.trim(); 35 | }; 36 | 37 | export default translateToHuman; 38 | -------------------------------------------------------------------------------- /public/twitterDark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/twitterfill.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/translateToSQL.js: -------------------------------------------------------------------------------- 1 | import fetch from "isomorphic-unfetch"; 2 | 3 | const translateToSQL = async (query, apiKey, tableSchema = "") => { 4 | 5 | // Validate inputs 6 | if (!query || !apiKey) { 7 | throw new Error("Missing query or API key."); 8 | } 9 | 10 | const prompt = `Translate this natural language query into SQL without changing the case of the entries given by me:\n\n"${query}"\n\n${tableSchema ? `Use this table schema:\n\n${tableSchema}\n\n` : ''}SQL Query:`; 11 | 12 | console.log(prompt); 13 | const response = await fetch("https://api.openai.com/v1/completions", { 14 | method: "POST", 15 | headers: { 16 | "Content-Type": "application/json", 17 | Authorization: `Bearer ${apiKey}`, 18 | }, 19 | body: JSON.stringify({ 20 | prompt, 21 | temperature: 0.5, 22 | max_tokens: 2048, 23 | n: 1, 24 | stop: "\\n", 25 | model: "gpt-3.5-turbo-instruct", 26 | frequency_penalty: 0.5, 27 | presence_penalty: 0.5, 28 | logprobs: 10, 29 | }), 30 | }); 31 | 32 | const data = await response.json(); 33 | if (!response.ok) { 34 | console.error("API Error:", response.status, data); 35 | throw new Error(data.error || "Error translating to SQL."); 36 | } 37 | 38 | return data.choices[0].text.trim(); 39 | }; 40 | 41 | export default translateToSQL; 42 | -------------------------------------------------------------------------------- /styles/loading-dots.module.css: -------------------------------------------------------------------------------- 1 | .loading { 2 | display: inline-flex; 3 | align-items: center; 4 | } 5 | 6 | .loading .spacer { 7 | margin-right: 2px; 8 | } 9 | 10 | .loading span { 11 | animation-name: blink; 12 | animation-duration: 1.4s; 13 | animation-iteration-count: infinite; 14 | animation-fill-mode: both; 15 | width: 5px; 16 | height: 5px; 17 | border-radius: 50%; 18 | display: inline-block; 19 | margin: 0 1px; 20 | } 21 | 22 | .loading span:nth-of-type(2) { 23 | animation-delay: 0.2s; 24 | } 25 | 26 | .loading span:nth-of-type(3) { 27 | animation-delay: 0.4s; 28 | } 29 | 30 | .loading2 { 31 | display: inline-flex; 32 | align-items: center; 33 | } 34 | 35 | .loading2 .spacer { 36 | margin-right: 2px; 37 | } 38 | 39 | .loading2 span { 40 | animation-name: blink; 41 | animation-duration: 1.4s; 42 | animation-iteration-count: infinite; 43 | animation-fill-mode: both; 44 | width: 4px; 45 | height: 4px; 46 | border-radius: 50%; 47 | display: inline-block; 48 | margin: 0 1px; 49 | } 50 | 51 | .loading2 span:nth-of-type(2) { 52 | animation-delay: 0.2s; 53 | } 54 | 55 | .loading2 span:nth-of-type(3) { 56 | animation-delay: 0.4s; 57 | } 58 | 59 | @keyframes blink { 60 | 0% { 61 | opacity: 0.2; 62 | } 63 | 20% { 64 | opacity: 1; 65 | } 66 | 100% { 67 | opacity: 0.2; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /components/Toggle.tsx: -------------------------------------------------------------------------------- 1 | interface Props { 2 | isUppercase: boolean; 3 | handleSwitchText: (textUpper: boolean) => void; 4 | } 5 | 6 | const Toggle: React.FC = ({ 7 | isUppercase, 8 | handleSwitchText: switchText, 9 | }) => { 10 | return ( 11 |
12 | 30 |
31 | ); 32 | }; 33 | 34 | export default Toggle; 35 | -------------------------------------------------------------------------------- /pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import Document, { Head, Html, Main, NextScript } from "next/document"; 2 | 3 | class MyDocument extends Document { 4 | render() { 5 | return ( 6 | 7 | 8 | 9 | 13 | 14 | 18 | 19 | 20 | 21 | 25 | 26 | 27 |
28 |
29 |
30 | 31 | 32 | 33 | ); 34 | } 35 | } 36 | 37 | export default MyDocument; 38 | -------------------------------------------------------------------------------- /hooks/useTranslate.ts: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | interface RequestBody { 4 | inputText: string; 5 | tableSchema?: string; 6 | } 7 | 8 | export function useTranslate() { 9 | const [translating, setTranslating] = useState(false); 10 | const [outputText, setOutputText] = useState(""); 11 | const [translationError, setTranslationError] = useState(""); 12 | 13 | const translate = async ({ 14 | inputText, 15 | tableSchema, 16 | isHumanToSql, 17 | }: { inputText: string; tableSchema: string; isHumanToSql: boolean }) => { 18 | setTranslating(true); 19 | try { 20 | const requestBody: RequestBody = { inputText }; 21 | if (tableSchema !== "") { 22 | requestBody.tableSchema = tableSchema; 23 | } 24 | const response = await fetch( 25 | `/api/${isHumanToSql ? "translate" : "sql-to-human"}`, 26 | { 27 | method: "POST", 28 | body: JSON.stringify(requestBody), 29 | headers: { "Content-Type": "application/json" }, 30 | }, 31 | ); 32 | if (response.ok) { 33 | const data = await response.json(); 34 | setOutputText(data.outputText); 35 | } else { 36 | setTranslationError( 37 | `Error translating ${isHumanToSql ? "to SQL" : "to human"}.`, 38 | ); 39 | } 40 | } catch (error) { 41 | console.error(error); 42 | setTranslationError( 43 | `Error translating ${isHumanToSql ? "to SQL" : "to human"}.`, 44 | ); 45 | } finally { 46 | setTranslating(false); 47 | } 48 | }; 49 | 50 | return { 51 | outputText, 52 | setOutputText, 53 | translate, 54 | translating, 55 | translationError, 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@fortawesome/fontawesome-svg-core": "^6.3.0", 10 | "@fortawesome/free-solid-svg-icons": "^6.3.0", 11 | "@fortawesome/react-fontawesome": "^0.2.0", 12 | "@headlessui/react": "^1.7.7", 13 | "@headlessui/tailwindcss": "^0.1.2", 14 | "@heroicons/react": "^2.0.13", 15 | "@tailwindcss/forms": "^0.5.3", 16 | "@tremor/react": "^3.6.5", 17 | "@vercel/analytics": "^0.1.11", 18 | "ajv": "^8.12.0", 19 | "eventsource-parser": "^0.0.5", 20 | "express-rate-limit": "^6.7.0", 21 | "isomorphic-unfetch": "^4.0.2", 22 | "next": "latest", 23 | "next-themes": "^0.2.1", 24 | "node-fetch": "^3.3.1", 25 | "openai": "^3.2.1", 26 | "react": "^18.2.0", 27 | "react-dom": "^18.2.0", 28 | "react-hook-form": "^7.42.0", 29 | "react-hot-toast": "^2.4.0", 30 | "react-syntax-highlighter": "^15.5.0", 31 | "react-use-measure": "^2.1.1", 32 | "redis": "^4.6.5", 33 | "styled-components": "^5.3.9", 34 | "vercel": "^30.2.2" 35 | }, 36 | "devDependencies": { 37 | "@types/node": "18.11.3", 38 | "@types/react": "18.0.21", 39 | "@types/react-dom": "18.0.6", 40 | "@types/react-syntax-highlighter": "^15.5.6", 41 | "@types/styled-components": "^5.1.26", 42 | "@typescript-eslint/eslint-plugin": "^5.55.0", 43 | "@typescript-eslint/parser": "^5.55.0", 44 | "autoprefixer": "^10.4.12", 45 | "eslint": "^8.36.0", 46 | "postcss": "^8.4.18", 47 | "tailwindcss": "^3.2.4", 48 | "typescript": "4.9.4" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer components { 6 | .blue-button-w-gradient-border { 7 | --blue500: theme(colors.blue.500); 8 | --blue700: theme(colors.blue.700); 9 | --blue800: theme(colors.blue.800); 10 | background: linear-gradient(to bottom right, var(--blue500), var(--blue700)) 11 | padding-box, 12 | linear-gradient(to bottom, var(--blue500), var(--blue800)) border-box; 13 | border: 1px solid transparent; 14 | } 15 | .light-button-w-gradient-border { 16 | --gray50: theme(colors.gray.50); 17 | background: linear-gradient(to bottom, var(--gray50), var(--gray50)) 18 | padding-box, 19 | linear-gradient(to bottom, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.27)) 20 | border-box; 21 | border: 1px solid transparent; 22 | } 23 | .dark-button-w-gradient-border { 24 | --gray50: theme(backgroundColor.custom-dark-gray); 25 | background: linear-gradient(to bottom, var(--gray50), var(--gray50)) 26 | padding-box, 27 | linear-gradient( 28 | to bottom, 29 | rgba(255, 255, 255, 0.2), 30 | rgba(255, 255, 255, 0.1) 31 | ) 32 | border-box; 33 | border: 1px solid transparent; 34 | } 35 | .container-w-gradient-border { 36 | background: linear-gradient(to bottom, white, white) padding-box, 37 | linear-gradient(to bottom, rgba(0, 0, 0, 0.06), rgba(0, 0, 0, 0.19)) 38 | border-box; 39 | border: 1px solid transparent; 40 | } 41 | .dark-container-w-gradient-border { 42 | --background: theme(backgroundColor.custom-gray); 43 | background: linear-gradient(to bottom, var(--background), var(--background)) 44 | padding-box, 45 | linear-gradient(to bottom, rgba(255, 255, 255, 0.1), rgba(0, 0, 0, 0.1)) 46 | border-box; 47 | border: 1px solid transparent; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /public/switch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /public/switchDark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /utils/OpenAIStream.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createParser, 3 | ParsedEvent, 4 | ReconnectInterval, 5 | } from "eventsource-parser"; 6 | 7 | export type ChatGPTModel = 8 | | "gpt-3.5-turbo" 9 | | "text-davinci-003" 10 | | "text-curie-001" 11 | | "text-babbage-001" 12 | | "text-ada-001"; 13 | 14 | export type ChatGPTAgent = "user" | "system"; 15 | 16 | export interface ChatGPTMessage { 17 | role: ChatGPTAgent; 18 | content: string; 19 | } 20 | 21 | export interface OpenAIStreamPayload { 22 | model: ChatGPTModel; 23 | messages: ChatGPTMessage[]; 24 | temperature: number; 25 | top_p: number; 26 | frequency_penalty: number; 27 | presence_penalty: number; 28 | max_tokens: number; 29 | stream: boolean; 30 | n: number; 31 | } 32 | 33 | export async function OpenAIStream(payload: OpenAIStreamPayload) { 34 | const encoder = new TextEncoder(); 35 | const decoder = new TextDecoder(); 36 | 37 | let counter = 0; 38 | 39 | const res = await fetch("https://api.openai.com/v1/chat/completions", { 40 | headers: { 41 | "Content-Type": "application/json", 42 | Authorization: `Bearer ${process.env.OPENAI_API_KEY ?? ""}`, 43 | }, 44 | method: "POST", 45 | body: JSON.stringify(payload), 46 | }); 47 | 48 | const stream = new ReadableStream({ 49 | async start(controller) { 50 | // callback 51 | function onParse(event: ParsedEvent | ReconnectInterval) { 52 | if (event.type === "event") { 53 | const data = event.data; 54 | // https://beta.openai.com/docs/api-reference/completions/create#completions/create-stream 55 | if (data === "[DONE]") { 56 | controller.close(); 57 | return; 58 | } 59 | try { 60 | const json = JSON.parse(data); 61 | const text = json.choices[0].delta?.content || ""; 62 | if (counter < 2 && (text.match(/\n/) || []).length) { 63 | // this is a prefix character (i.e., "\n\n"), do nothing 64 | return; 65 | } 66 | const queue = encoder.encode(text); 67 | controller.enqueue(queue); 68 | counter++; 69 | } catch (e) { 70 | // maybe parse error 71 | controller.error(e); 72 | } 73 | } 74 | } 75 | 76 | // stream response (SSE) from OpenAI may be fragmented into multiple chunks 77 | // this ensures we properly read chunks and invoke an event for each SSE event stream 78 | const parser = createParser(onParse); 79 | // https://web.dev/streams/#asynchronous-iteration 80 | for await (const chunk of res.body as ReadableStream & 81 | AsyncIterable) { 82 | parser.feed(decoder.decode(chunk)); 83 | } 84 | }, 85 | }); 86 | 87 | return stream; 88 | } 89 | -------------------------------------------------------------------------------- /components/Header/Header.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import Github from "../GitHub"; 3 | import ThemeButton from "../ThemeButton"; 4 | import { useTheme } from "next-themes"; 5 | 6 | export const Header = () => { 7 | const { resolvedTheme } = useTheme(); 8 | const svgFillColor = resolvedTheme === "dark" ? "#D8D8D8" : "black"; 9 | const btnBgColor = 10 | resolvedTheme === "dark" 11 | ? "dark-button-w-gradient-border" 12 | : "light-button-w-gradient-border"; 13 | 14 | return ( 15 |
16 | 17 |

sqlTranslate

18 |

19 | Human Language to SQL Translator 20 |

21 | 22 | 59 |
60 | ); 61 | }; 62 | 63 | -------------------------------------------------------------------------------- /components/atoms/Orbs.tsx: -------------------------------------------------------------------------------- 1 | export const WhiteOrb = () => ( 2 | 10 | 11 | 15 | 16 | 17 | 26 | 27 | 33 | 37 | 38 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | ); 53 | 54 | export const GreenOrb = () => ( 55 | 63 | 67 | 68 | 76 | 77 | 78 | 79 | 80 | 81 | ); 82 | 83 | export const OrangeOrb = () => ( 84 | 92 | 96 | 97 | 105 | 106 | 107 | 108 | 109 | 110 | ); 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

SQL Translator
(SQL to Natural Language and Natural Language to SQL)

2 | 3 | Welcome to the SQL and Natural Language Translator! This tool is designed to make it easy for anyone to translate SQL (Structured Query Language) commands into natural language and vice versa. SQL is a programming language used to manage and manipulate data in relational databases, and while it's a powerful tool, it can also be quite complex and difficult to understand. On the other hand, natural language is the language that we speak and write in everyday life, and it's often the preferred way to communicate for people who are not familiar with technical jargon. 4 | 5 | With the SQL and Natural Language Translator, you don't need to be a SQL expert to understand what's going on in your database, or to write SQL queries. You can simply type in your query in natural language and get the corresponding SQL code, or type in your SQL code and get a human-readable translation.This project is 100% free and open source. 6 | 7 |
8 |
9 | 10 |
11 | 12 | ## 🌟 Features 13 | 14 | - Dark mode 15 | - Lowercase/uppercase toggle 16 | - Copy to clipboard 17 | - SQL syntax highlighting 18 | - Schema awareness (beta) 19 | - Query history 20 | 21 | 22 | ## 📖 How to use: 23 | 24 | Using the SQL to Natural Language Translator is easy. Simply navigate to the tool's website and choose whether you want to translate from natural language to SQL or from SQL to natural language. Then, type in your query and hit the "translate" button. The tool will generate the corresponding code or text and display it on the screen. 25 | You can press the 'reverse' button to give input as Natural Language and get SQL queries in response 26 | 27 | 28 | ## 🎯 Roadmap 29 | 30 | - [ ] Functions (WIP) 31 | - [ ] Procedures 32 | 33 | 34 | ## 🛠️ Installation 35 | 36 | ### Local Development Environment 37 | 38 | 1. Clone the repository: 39 | 40 | ```bash 41 | git clone https://github.com/whoiskatrin/sql-translator.git 42 | ``` 43 | 44 | 2. Install the required packages: 45 | 46 | ```bash 47 | cd sql-translator 48 | npm install 49 | ``` 50 | 51 | 3. Build the application: 52 | 53 | ```bash 54 | npm run build 55 | ``` 56 | 57 | 4. Input your OPENAI API key in the .env file, you can get your API key [here](https://beta.openai.com/account/api-keys): 58 | 59 | ```bash 60 | OPENAI_API_KEY=$YOUR_API_KEY 61 | ``` 62 | 63 | 5. Start the development server: 64 | 65 | ```bash 66 | npm start 67 | ``` 68 | 69 | ### Docker Compose 70 | 71 | 1. Clone the repository: 72 | 73 | ```bash 74 | git clone https://github.com/whoiskatrin/sql-translator.git 75 | ``` 76 | 77 | 2. Input your OPENAI API key in the .env.production file, you can get your API key [here](https://beta.openai.com/account/api-keys): 78 | 79 | ```bash 80 | OPENAI_API_KEY=$YOUR_API_KEY 81 | ``` 82 | 83 | 3. Start the development server: 84 | 85 | ```bash 86 | docker-compose up 87 | ``` 88 | 89 | ## 🖥️ Usage 90 | 91 | Once the development server is running, you can access the application by navigating to `http://localhost:3000` in your web browser. 92 | 93 | Enter a natural language query in the input box and click "Translate" to generate the corresponding SQL code. The generated SQL code will be displayed in the output box. 94 | 95 | ## 👥 Contributing 96 | 97 | Contributions to SQL Translator are welcome and encouraged! To contribute, please follow these steps: 98 | 99 | 1. Fork the repository 100 | 2. Create a new branch 101 | 3. Make your changes 102 | 4. Push your changes to your fork 103 | 5. Submit a pull request 104 | 105 | ## 📜 License 106 | 107 | SQL Translator is released under the MIT [License](LICENSE). 108 | -------------------------------------------------------------------------------- /.history/README_20230615141736.md: -------------------------------------------------------------------------------- 1 |

SQL Translator
(SQL to Natural Language and Natural Language to SQL)

2 | 3 | Welcome to the SQL and Natural Language Translator! This tool is designed to make it easy for anyone to translate SQL (Structured Query Language) commands into natural language and vice versa. SQL is a programming language used to manage and manipulate data in relational databases, and while it's a powerful tool, it can also be quite complex and difficult to understand. On the other hand, natural language is the language that we speak and write in everyday life, and it's often the preferred way to communicate for people who are not familiar with technical jargon. 4 | 5 | With the SQL and Natural Language Translator, you don't need to be a SQL expert to understand what's going on in your database, or to write SQL queries. You can simply type in your query in natural language and get the corresponding SQL code, or type in your SQL code and get a human-readable translation.This project is 100% free and open source. 6 | 7 |
8 |
9 | 10 |
11 | 12 | ## 🌟 Features 13 | 14 | - Dark mode 15 | - Lowercase/uppercase toggle 16 | - Copy to clipboard 17 | - SQL syntax highlighting 18 | - Schema awareness (beta) 19 | - Query history 20 | 21 | 22 | ## 📖 How to use: 23 | 24 | Using the SQL to Natural Language Translator is easy. Simply navigate to the tool's website and choose whether you want to translate from natural language to SQL or from SQL to natural language. Then, type in your query and hit the "translate" button. The tool will generate the corresponding code or text and display it on the screen. 25 | You can press the 'reverse' button to give input as Natural Language and get SQL queries in response 26 | 27 | 28 | ## 🎯 Roadmap 29 | 30 | - [ ] Functions 31 | - [ ] Procedures 32 | 33 | 34 | ## 🛠️ Installation 35 | 36 | ### Local Development Environment 37 | 38 | 1. Clone the repository: 39 | 40 | ```bash 41 | git clone https://github.com/whoiskatrin/sql-translator.git 42 | ``` 43 | 44 | 2. Install the required packages: 45 | 46 | ```bash 47 | cd sql-translator 48 | npm install 49 | ``` 50 | 51 | 3. Build the application: 52 | 53 | ```bash 54 | npm run build 55 | ``` 56 | 57 | 4. Input your OPENAI API key in the .env file, you can get your API key [here](https://beta.openai.com/account/api-keys): 58 | 59 | ```bash 60 | OPENAI_API_KEY=$YOUR_API_KEY 61 | ``` 62 | 63 | 5. Start the development server: 64 | 65 | ```bash 66 | npm start 67 | ``` 68 | 69 | ### Docker Compose 70 | 71 | 1. Clone the repository: 72 | 73 | ```bash 74 | git clone https://github.com/whoiskatrin/sql-translator.git 75 | ``` 76 | 77 | 2. Input your OPENAI API key in the .env.production file, you can get your API key [here](https://beta.openai.com/account/api-keys): 78 | 79 | ```bash 80 | OPENAI_API_KEY=$YOUR_API_KEY 81 | ``` 82 | 83 | 3. Start the development server: 84 | 85 | ```bash 86 | docker-compose up 87 | ``` 88 | 89 | ## 🖥️ Usage 90 | 91 | Once the development server is running, you can access the application by navigating to `http://localhost:3000` in your web browser. 92 | 93 | Enter a natural language query in the input box and click "Translate" to generate the corresponding SQL code. The generated SQL code will be displayed in the output box. 94 | 95 | ## 👥 Contributing 96 | 97 | Contributions to SQL Translator are welcome and encouraged! To contribute, please follow these steps: 98 | 99 | 1. Fork the repository 100 | 2. Create a new branch 101 | 3. Make your changes 102 | 4. Push your changes to your fork 103 | 5. Submit a pull request 104 | 105 | ## 📜 License 106 | 107 | SQL Translator is released under the MIT [License](LICENSE). 108 | -------------------------------------------------------------------------------- /.history/README_20230615141805.md: -------------------------------------------------------------------------------- 1 |

SQL Translator
(SQL to Natural Language and Natural Language to SQL)

2 | 3 | Welcome to the SQL and Natural Language Translator! This tool is designed to make it easy for anyone to translate SQL (Structured Query Language) commands into natural language and vice versa. SQL is a programming language used to manage and manipulate data in relational databases, and while it's a powerful tool, it can also be quite complex and difficult to understand. On the other hand, natural language is the language that we speak and write in everyday life, and it's often the preferred way to communicate for people who are not familiar with technical jargon. 4 | 5 | With the SQL and Natural Language Translator, you don't need to be a SQL expert to understand what's going on in your database, or to write SQL queries. You can simply type in your query in natural language and get the corresponding SQL code, or type in your SQL code and get a human-readable translation.This project is 100% free and open source. 6 | 7 |
8 |
9 | 10 |
11 | 12 | ## 🌟 Features 13 | 14 | - Dark mode 15 | - Lowercase/uppercase toggle 16 | - Copy to clipboard 17 | - SQL syntax highlighting 18 | - Schema awareness (beta) 19 | - Query history 20 | 21 | 22 | ## 📖 How to use: 23 | 24 | Using the SQL to Natural Language Translator is easy. Simply navigate to the tool's website and choose whether you want to translate from natural language to SQL or from SQL to natural language. Then, type in your query and hit the "translate" button. The tool will generate the corresponding code or text and display it on the screen. 25 | You can press the 'reverse' button to give input as Natural Language and get SQL queries in response 26 | 27 | 28 | ## 🎯 Roadmap 29 | 30 | - [ ] Functions (WIP) 31 | - [ ] Procedures 32 | 33 | 34 | ## 🛠️ Installation 35 | 36 | ### Local Development Environment 37 | 38 | 1. Clone the repository: 39 | 40 | ```bash 41 | git clone https://github.com/whoiskatrin/sql-translator.git 42 | ``` 43 | 44 | 2. Install the required packages: 45 | 46 | ```bash 47 | cd sql-translator 48 | npm install 49 | ``` 50 | 51 | 3. Build the application: 52 | 53 | ```bash 54 | npm run build 55 | ``` 56 | 57 | 4. Input your OPENAI API key in the .env file, you can get your API key [here](https://beta.openai.com/account/api-keys): 58 | 59 | ```bash 60 | OPENAI_API_KEY=$YOUR_API_KEY 61 | ``` 62 | 63 | 5. Start the development server: 64 | 65 | ```bash 66 | npm start 67 | ``` 68 | 69 | ### Docker Compose 70 | 71 | 1. Clone the repository: 72 | 73 | ```bash 74 | git clone https://github.com/whoiskatrin/sql-translator.git 75 | ``` 76 | 77 | 2. Input your OPENAI API key in the .env.production file, you can get your API key [here](https://beta.openai.com/account/api-keys): 78 | 79 | ```bash 80 | OPENAI_API_KEY=$YOUR_API_KEY 81 | ``` 82 | 83 | 3. Start the development server: 84 | 85 | ```bash 86 | docker-compose up 87 | ``` 88 | 89 | ## 🖥️ Usage 90 | 91 | Once the development server is running, you can access the application by navigating to `http://localhost:3000` in your web browser. 92 | 93 | Enter a natural language query in the input box and click "Translate" to generate the corresponding SQL code. The generated SQL code will be displayed in the output box. 94 | 95 | ## 👥 Contributing 96 | 97 | Contributions to SQL Translator are welcome and encouraged! To contribute, please follow these steps: 98 | 99 | 1. Fork the repository 100 | 2. Create a new branch 101 | 3. Make your changes 102 | 4. Push your changes to your fork 103 | 5. Submit a pull request 104 | 105 | ## 📜 License 106 | 107 | SQL Translator is released under the MIT [License](LICENSE). 108 | -------------------------------------------------------------------------------- /.history/README_20230615141801.md: -------------------------------------------------------------------------------- 1 |

SQL Translator
(SQL to Natural Language and Natural Language to SQL)

2 | 3 | Welcome to the SQL and Natural Language Translator! This tool is designed to make it easy for anyone to translate SQL (Structured Query Language) commands into natural language and vice versa. SQL is a programming language used to manage and manipulate data in relational databases, and while it's a powerful tool, it can also be quite complex and difficult to understand. On the other hand, natural language is the language that we speak and write in everyday life, and it's often the preferred way to communicate for people who are not familiar with technical jargon. 4 | 5 | With the SQL and Natural Language Translator, you don't need to be a SQL expert to understand what's going on in your database, or to write SQL queries. You can simply type in your query in natural language and get the corresponding SQL code, or type in your SQL code and get a human-readable translation.This project is 100% free and open source. 6 | 7 |
8 |
9 | 10 |
11 | 12 | ## 🌟 Features 13 | 14 | - Dark mode 15 | - Lowercase/uppercase toggle 16 | - Copy to clipboard 17 | - SQL syntax highlighting 18 | - Schema awareness (beta) 19 | - Query history 20 | 21 | 22 | ## 📖 How to use: 23 | 24 | Using the SQL to Natural Language Translator is easy. Simply navigate to the tool's website and choose whether you want to translate from natural language to SQL or from SQL to natural language. Then, type in your query and hit the "translate" button. The tool will generate the corresponding code or text and display it on the screen. 25 | You can press the 'reverse' button to give input as Natural Language and get SQL queries in response 26 | 27 | 28 | ## 🎯 Roadmap 29 | 30 | - [ ] Functions (in progress) 31 | - [ ] Procedures 32 | 33 | 34 | ## 🛠️ Installation 35 | 36 | ### Local Development Environment 37 | 38 | 1. Clone the repository: 39 | 40 | ```bash 41 | git clone https://github.com/whoiskatrin/sql-translator.git 42 | ``` 43 | 44 | 2. Install the required packages: 45 | 46 | ```bash 47 | cd sql-translator 48 | npm install 49 | ``` 50 | 51 | 3. Build the application: 52 | 53 | ```bash 54 | npm run build 55 | ``` 56 | 57 | 4. Input your OPENAI API key in the .env file, you can get your API key [here](https://beta.openai.com/account/api-keys): 58 | 59 | ```bash 60 | OPENAI_API_KEY=$YOUR_API_KEY 61 | ``` 62 | 63 | 5. Start the development server: 64 | 65 | ```bash 66 | npm start 67 | ``` 68 | 69 | ### Docker Compose 70 | 71 | 1. Clone the repository: 72 | 73 | ```bash 74 | git clone https://github.com/whoiskatrin/sql-translator.git 75 | ``` 76 | 77 | 2. Input your OPENAI API key in the .env.production file, you can get your API key [here](https://beta.openai.com/account/api-keys): 78 | 79 | ```bash 80 | OPENAI_API_KEY=$YOUR_API_KEY 81 | ``` 82 | 83 | 3. Start the development server: 84 | 85 | ```bash 86 | docker-compose up 87 | ``` 88 | 89 | ## 🖥️ Usage 90 | 91 | Once the development server is running, you can access the application by navigating to `http://localhost:3000` in your web browser. 92 | 93 | Enter a natural language query in the input box and click "Translate" to generate the corresponding SQL code. The generated SQL code will be displayed in the output box. 94 | 95 | ## 👥 Contributing 96 | 97 | Contributions to SQL Translator are welcome and encouraged! To contribute, please follow these steps: 98 | 99 | 1. Fork the repository 100 | 2. Create a new branch 101 | 3. Make your changes 102 | 4. Push your changes to your fork 103 | 5. Submit a pull request 104 | 105 | ## 📜 License 106 | 107 | SQL Translator is released under the MIT [License](LICENSE). 108 | -------------------------------------------------------------------------------- /public/stars.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import dynamic from "next/dynamic"; 3 | import { Button } from "@tremor/react"; 4 | const SyntaxHighlighter = dynamic(() => import("react-syntax-highlighter")); 5 | import { vs, dracula } from "react-syntax-highlighter/dist/cjs/styles/hljs"; 6 | import Head from "next/head"; 7 | import { Analytics } from "@vercel/analytics/react"; 8 | import ThemeButton from "../components/ThemeButton"; 9 | import { useTranslate } from "../hooks/useTranslate"; 10 | import { toast } from "react-hot-toast"; 11 | import LoadingDots from "../components/LoadingDots"; 12 | import { useTheme } from "next-themes"; 13 | import Toggle from "../components/Toggle"; 14 | import { Header } from "../components/Header/Header"; 15 | import { GreenOrb, OrangeOrb, WhiteOrb } from "../components/atoms/Orbs"; 16 | interface IHistory { 17 | inputText: string; 18 | outputText: string; 19 | tableSchema?: string; 20 | isHumanToSql?: boolean; 21 | } 22 | 23 | interface IHistoryEntry { 24 | inputText: string; 25 | outputText: string; 26 | tableSchema?: string; 27 | isHumanToSql?: boolean; 28 | } 29 | 30 | interface ITextCopied { 31 | isCopied: boolean; 32 | isHistory: boolean; 33 | text: string; 34 | } 35 | interface prev { 36 | previnput: string; 37 | prevoutput: string; 38 | } 39 | 40 | export default function Home() { 41 | const { resolvedTheme } = useTheme(); 42 | const isThemeDark = resolvedTheme === "dark"; 43 | const [mounted, setMounted] = useState(false); 44 | const { 45 | translate, 46 | translating, 47 | outputText, 48 | setOutputText, 49 | translationError, 50 | } = useTranslate(); 51 | const [inputText, setInputText] = useState(""); 52 | const [prevquery, setPrevquery] = useState([]); 53 | const [isHumanToSql, setIsHumanToSql] = useState(true); 54 | const [isOutputTextUpperCase, setIsOutputTextUpperCase] = useState(false); 55 | const [tableSchema, setTableSchema] = useState(""); 56 | const [showTableSchema, setShowTableSchema] = useState(false); 57 | const [history, setHistory] = useState([]); 58 | const [showHistory, setShowHistory] = useState(false); 59 | const [hasTranslated, setHasTranslated] = useState(false); 60 | const [copied, setCopied] = useState(); 61 | 62 | useEffect(() => { 63 | if (inputText && hasTranslated) { 64 | setHistory((prevHistory) => [ 65 | ...prevHistory, 66 | { 67 | inputText: JSON.stringify(inputText), 68 | outputText: JSON.stringify(outputText), 69 | tableSchema, 70 | isHumanToSql, 71 | }, 72 | ]); 73 | 74 | addHistoryEntry({ 75 | inputText: JSON.stringify(inputText), 76 | tableSchema, 77 | isHumanToSql, 78 | outputText: JSON.stringify(outputText), 79 | }); 80 | 81 | setHasTranslated(false); 82 | } 83 | }, [outputText]); 84 | 85 | useEffect(() => { 86 | setMounted(true); 87 | }, []); 88 | 89 | useEffect(() => { 90 | if (translationError) toast.error(translationError); 91 | }, [translationError]); 92 | 93 | if (!mounted) { 94 | return null; 95 | } 96 | 97 | const isValidTableSchema = (text: string) => { 98 | const pattern = /^CREATE\s+TABLE\s+\w+\s*\((\s*.+\s*,?\s*)+\);?$/i; 99 | const regex = new RegExp(pattern); 100 | return regex.test(text.trim()); 101 | }; 102 | 103 | const addHistoryEntry = (entry: IHistory) => { 104 | if ( 105 | !history.some( 106 | ({ inputText, outputText }) => 107 | inputText === entry.inputText && outputText === entry.outputText 108 | ) && 109 | !prevquery.some( 110 | ({ previnput, prevoutput }) => 111 | previnput === entry.inputText && prevoutput === entry.outputText 112 | ) 113 | ) { 114 | setHistory([...history, entry]); 115 | } 116 | const newhistory: prev = { 117 | previnput: entry.inputText, 118 | prevoutput: entry.outputText, 119 | }; 120 | setPrevquery([...prevquery, newhistory]); 121 | }; 122 | 123 | function safeJSONParse(str: string) { 124 | try { 125 | return JSON.parse(str); 126 | } catch (e) { 127 | console.error("JSON parse error:", e); 128 | return null; 129 | } 130 | } 131 | 132 | const handleInputChange = (event: React.ChangeEvent) => { 133 | setInputText(event.target.value); 134 | if (!showTableSchema) { 135 | setTableSchema(""); 136 | } 137 | }; 138 | 139 | const handleCopy = (text: string, isHistory: boolean) => { 140 | navigator.clipboard.writeText(text); 141 | setCopied({ 142 | isCopied: true, 143 | isHistory: isHistory, 144 | text: text, 145 | }); 146 | setTimeout(() => { 147 | setCopied({ 148 | isCopied: false, 149 | isHistory: isHistory, 150 | text: text, 151 | }); 152 | }, 3000); 153 | }; 154 | 155 | const buttonStyles = { 156 | light: "light-button-w-gradient-border text-black", 157 | dark: "dark-button-w-gradient-border text-[#D8D8D8]", 158 | }; 159 | 160 | const handleSubmit = async (event: React.FormEvent) => { 161 | event.preventDefault(); 162 | 163 | try { 164 | // Validate input syntax 165 | if (!isHumanToSql) { 166 | const pattern = 167 | /^\s*(select|insert|update|delete|create|alter|drop|truncate|grant|revoke|use|begin|commit|rollback)\s/i; 168 | const regex = new RegExp(pattern); 169 | if (!regex.test(inputText)) { 170 | toast.error("Invalid SQL syntax."); 171 | return; 172 | } 173 | } 174 | if (showTableSchema && !isValidTableSchema(tableSchema)) { 175 | toast.error("Invalid table schema."); 176 | return; 177 | } 178 | 179 | translate({ inputText, tableSchema, isHumanToSql }); 180 | setHasTranslated(true); 181 | } catch (error) { 182 | console.log(error); 183 | toast.error(`Error translating ${isHumanToSql ? "to SQL" : "to human"}.`); 184 | } 185 | }; 186 | 187 | return ( 188 |
189 |
190 | 191 | 192 | {isHumanToSql ? "Human to SQL Translator" : "SQL to Human Translator"} 193 | 194 | 195 | 196 | 197 | 198 |
199 |
200 |
handleSubmit(event)} 202 | className="rounded-xl bg-white dark:bg-custom-gray container-w-gradient-border dark:dark-container-w-gradient-border p-3 h-full w-full" 203 | > 204 |
205 | 211 |