diff --git a/package-lock.json b/package-lock.json index aee170d..9f949ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "react-dom": "^18.2.0", "react-query": "^3.39.3", "react-simple-code-editor": "^0.13.1", + "tailwind-merge": "^2.0.0", "tailwindcss": "3.3.3", "zustand": "^4.4.1" }, @@ -4824,6 +4825,18 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/tailwind-merge": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.0.0.tgz", + "integrity": "sha512-WO8qghn9yhsldLSg80au+3/gY9E4hFxIvQ3qOmlpXnqpDKoMruKfi/56BbbMg6fHTQJ9QD3cc79PoWqlaQE4rw==", + "dependencies": { + "@babel/runtime": "^7.23.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz", diff --git a/package.json b/package.json index 7efbeb1..d0a51b3 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "react-dom": "^18.2.0", "react-query": "^3.39.3", "react-simple-code-editor": "^0.13.1", + "tailwind-merge": "^2.0.0", "tailwindcss": "3.3.3", "zustand": "^4.4.1" }, @@ -42,6 +43,4 @@ "printWidth": 120, "tabWidth": 4 } - - } diff --git a/src/app/components/Container.tsx b/src/app/components/Container.tsx index 39b0d93..b9c60c4 100644 --- a/src/app/components/Container.tsx +++ b/src/app/components/Container.tsx @@ -1,15 +1,17 @@ +import { twMerge } from 'tailwind-merge'; + interface ContainerTypes { className?: string; children: React.JSX.Element[] | React.JSX.Element; } const Container = ({ children, className }: ContainerTypes) => { - const classesName = `${className} flex-auto flex flex-col flex-initial - bg-gray-400 px-5 py-3 h-5/6 - rounded-lg bg-opacity-20 - backdrop-blur-lg - hover:shadow-lg hover:shadow-blue-950`; + const classesName = twMerge('min-h-full', className); return
{children}
; }; +Container.defaultProps = { + className: '', +}; + export default Container; diff --git a/src/app/components/Examples.tsx b/src/app/components/Examples.tsx index 9503f38..c74ca37 100644 --- a/src/app/components/Examples.tsx +++ b/src/app/components/Examples.tsx @@ -1,52 +1,17 @@ -import { ShotsTypes, userInputsArr } from '@/store/inputStore'; +import { userInputsArr } from '@/store/inputStore'; import Shots from './Shots'; -import ButtonsEx from './ExamplesButtons'; - -interface ExampleTypes { - send: (examples: ShotsTypes[], lastPrompt: string) => Promise; - isLoading: boolean; -} // examples are built with one user input and result -const Examples = ({ send, isLoading }: ExampleTypes) => { - const [deleteUserInputArr, examples, userInputs, updateUserInput, updateUserResults] = userInputsArr((state) => [ - state.deleteUserInputArr, - state.examples, - state.userInputs, - state.updateUserInput, - state.updateUserResults, - ]); - - const handleDeleteAll = () => { - deleteUserInputArr(); - updateUserInput(''); - updateUserResults(''); - }; - - const onClickHandler = async () => { - await send(examples, userInputs); - }; - - // 1 shot minimun must be added to send a response request to GPT - const disabled = !(examples.length >= 1 && userInputs !== '' && !isLoading); - const classNameForSend = disabled ? ' text-gray-400' : ' hover:bg-yellow-800'; - const classNameForButton = isLoading ? ' text-gray-400' : ' hover:bg-red-900'; +const Examples = () => { + const [examples] = userInputsArr((state) => [state.examples]); return ( - <> -

Examples

-
+
+

Examples

+
- - +
); }; diff --git a/src/app/components/ExamplesButtons.tsx b/src/app/components/ExamplesButtons.tsx deleted file mode 100644 index db90b65..0000000 --- a/src/app/components/ExamplesButtons.tsx +++ /dev/null @@ -1,40 +0,0 @@ -/* eslint-disable @typescript-eslint/no-misused-promises */ -import React from 'react'; - -interface interfaceButtonsEx { - classNameForButton: string; - classNameForSend: string; - handleDeleteAll: () => void; - isLoading: boolean; - disabled: boolean; - onClickHandler: () => Promise | void; -} - -const ButtonsEx = ({ - classNameForButton, - classNameForSend, - handleDeleteAll, - isLoading, - disabled, - onClickHandler, -}: interfaceButtonsEx) => ( -
- - -
-); -export default ButtonsEx; diff --git a/src/app/components/Inputs.tsx b/src/app/components/Inputs.tsx index e5c19e9..38ec7f9 100644 --- a/src/app/components/Inputs.tsx +++ b/src/app/components/Inputs.tsx @@ -9,28 +9,18 @@ interface InputsType { } const Inputs = ({ data, isLoading }: InputsType) => { - const [updateUserInputArr, userInput, updateUserInput, userResults, updateUserResults] = userInputsArr((state) => [ - state.updateUserInputArr, + const [userInput, updateUserInput, userResults, updateUserResults] = userInputsArr((state) => [ state.userInputs, state.updateUserInput, state.userResults, state.updateUserResults, ]); - const clickedAdd = () => { - if (userInput.trim() !== '' && userResults.trim() !== '') { - updateUserInputArr({ input: userInput, result: userResults }); - updateUserInput(''); - updateUserResults(''); - } - }; - return ( - + ( - <> -

Input

-
+
+

Model

+
highlight(userInputWritten, languages.tsx, 'tsx')} onValueChange={(userInputWritten) => updateUserInput(userInputWritten)} - className='resize-none' - style={{ - minHeight: '100%', - }} + className='min-h-full' preClassName='!break-all ' textareaClassName='!break-all ' padding={7} @@ -32,7 +29,7 @@ export const Model = ({ userInput, updateUserInput, disabled }: ModelTypes) => ( placeholder='Input some code...' />
- +
); export default Model; diff --git a/src/app/components/Result.tsx b/src/app/components/Result.tsx index d2b66e4..8867655 100644 --- a/src/app/components/Result.tsx +++ b/src/app/components/Result.tsx @@ -8,7 +8,6 @@ import 'prismjs/themes/prism-tomorrow.css'; interface ResultTypes { data: string | undefined | null; - clickedAdd: () => void; userResults: string; updateUserResults: (input: string) => void; disabled: boolean; @@ -16,25 +15,24 @@ interface ResultTypes { } // results are GPT responses exemplified by the user -export const Result = ({ data, clickedAdd, userResults, updateUserResults, disabled, isLoading }: ResultTypes) => { +export const Result = ({ data, userResults, updateUserResults, disabled, isLoading }: ResultTypes) => { useEffect(() => { if (typeof data === 'string') { updateUserResults(data); } - }, [data]); + }, [data, updateUserResults]); const textAreaClassName = isLoading ? 'animate-pulse' : ''; - const buttonClassName = disabled ? ' text-gray-400' : ' hover:bg-slate-600'; return ( - <> -

Result

-
+
+

Result

+
highlight(userResultsWritten, languages.tsx, 'tsx')} onValueChange={(userResultsWritten) => updateUserResults(userResultsWritten)} - className={`min-h-full text-black bg-white w-full rounded p-2 ${textAreaClassName}`} + className={`min-h-full text-black bg-white ${textAreaClassName}`} padding={7} preClassName='!break-all' textareaClassName='!break-all' @@ -42,17 +40,7 @@ export const Result = ({ data, clickedAdd, userResults, updateUserResults, disab placeholder='Input a result...' />
-
- -
- +
); }; diff --git a/src/app/components/Shots.tsx b/src/app/components/Shots.tsx index a8307e2..a444275 100644 --- a/src/app/components/Shots.tsx +++ b/src/app/components/Shots.tsx @@ -15,8 +15,8 @@ const Shots = ({ examples }: ShotsType) => { if (!(examples.length > 0)) { return ( -
- No examples registered +
+ No examples added
); } else { diff --git a/src/app/components/ToolBar.tsx b/src/app/components/ToolBar.tsx new file mode 100644 index 0000000..ea37496 --- /dev/null +++ b/src/app/components/ToolBar.tsx @@ -0,0 +1,130 @@ +/* eslint-disable react-hooks/rules-of-hooks */ +/* eslint-disable @typescript-eslint/no-misused-promises */ +import React, { useState } from 'react'; +import { ShotsTypes, userInputsArr } from '../../store/inputStore'; + +interface toolBarInter { + isLoading: boolean; + send: (examples: ShotsTypes[], userInputs: string) => Promise; +} + +export const ToolBar = ({ isLoading, send }: toolBarInter) => { + const [ + updateUserInputArr, + userInput, + updateUserInput, + userResults, + updateUserResults, + examples, + deleteUserInputArr, + loadDefaultExample, + ] = userInputsArr((state) => [ + state.updateUserInputArr, + state.userInputs, + state.updateUserInput, + state.userResults, + state.updateUserResults, + state.examples, + state.deleteUserInputArr, + state.loadDefaultExample, + ]); + + const [mode, useMode] = useState(true); + + const buttonClassName = isLoading ? ' text-gray-400' : ' hover:bg-slate-600'; + + const disabledSendRequest = !(examples.length >= 1 && userInput !== '' && !isLoading); + const classNameForSend = disabledSendRequest ? ' text-gray-400' : ' hover:bg-yellow-800'; + const classNameForButton = isLoading ? ' text-gray-400' : ' hover:bg-red-900'; + + const clickedAdd = () => { + if (userInput.trim() !== '' && userResults.trim() !== '') { + updateUserInputArr({ input: userInput, result: userResults }); + updateUserInput(''); + updateUserResults(''); + } + }; + + const handleDeleteAll = () => { + deleteUserInputArr(); + updateUserInput(''); + updateUserResults(''); + }; + + const onClickHandler = async () => { + await send(examples, userInput); + }; + + const onModeClick = (bol: boolean) => { + useMode(bol); + }; + + return ( +
+
+ {mode ? ( + <> + + + + ) : ( + + )} + +
+

Input mode

+
+ + +
+
+
+ + +
+ ); +}; + +export default ToolBar; diff --git a/src/app/examples.json b/src/app/examples.json new file mode 100644 index 0000000..8b982c3 --- /dev/null +++ b/src/app/examples.json @@ -0,0 +1,17 @@ +{ + "examples":[ + { + "input":"const rows = [\n\t{ id: 1, lastName: 'Snow', firstName: 'Jon', age: 35 },\n\t{ id: 2, lastName: 'Lannister', firstName: 'Cersei', age: 42 },\n\t{ id: 3, lastName: 'Lannister', firstName: 'Jaime', age: 45 },\n\t{ id: 4, lastName: 'Stark', firstName: 'Arya', age: 16 },\n\t{ id: 5, lastName: 'Targaryen', firstName: 'Daenerys', age: null },\n\t{ id: 6, lastName: 'Melisandre', firstName: null, age: 150 },\n\t{ id: 7, lastName: 'Clifford', firstName: 'Ferrara', age: 44 },\n\t{ id: 8, lastName: 'Frances', firstName: 'Rossini', age: 36 },\n\t{ id: 9, lastName: 'Roxie', firstName: 'Harvey', age: 65 },\n];", + "result":"const columns: GridColDef[] = [\n\t{ field: 'id', headerName: 'ID', width: 90 },\n\t{\n\t\tfield: 'firstName',\n\t\theaderName: 'First name',\n\t\twidth: 150,\n\t\teditable: true,\n\t},\n\t{\n\t\tfield: 'lastName',\n\t\theaderName:'Last name',\n\t\twidth: 150,\n\t\teditable: true,\n\t},\n\t{\n\t\tfield: 'age',\n\t\theaderName: 'Age',\n\t\ttype: 'number',\n\t\twidth: 110,\n\t\teditable: true,\n\t},\n\t{\n\t\tfield: 'fullName',\n\t\theaderName: 'Full name',\n\t\tdescription: 'This column has a value getter and is not sortable.',\n\t\tsortable: false,\n\t\twidth: 160,\n\t\tvalueGetter: (params: GridValueGetterParams) =>\n\t\t\t`${params.row.firstName || ''}${params.row.lastName || ''}`,\n\t},\n];" + }, + { + "input":"type CsCapacity = {\n\tName: string;\n\tSeniority: string;\n\tAccountsAssigned: number;\n\tBaselineCapacity: number;\n\tDpsSupported: number;\n\tCurrentCapacity: number;\n};", + "result":"const getCapacityData = (data?: CsCapacity[]) =>\n\tsafeMap(data, (entry) => [\n\t\tentry.Name,\n\t\tentry.Seniority,\n\t\tentry.AccountsAssigned,\n\t\tentry.BaselineCapacity,\n\t\tentry.DpsSupported,\n\t\tentry.CurrentCapacity,\n\t]);" + + }, + { + "input":"const cerealsObj:any[] = [\n\t{\n\t\taddItemsSold:function (addUp:number){\n\t\t\tthis.attributes[0].itemsSold += addUp;\n\t\t},\n\t\tname:'Zucaritas',\n\t\tattributes:[\n\t\t\t{\n\t\t\t\t\tindex:0,\n\t\t\t\t\titemsSold:90,\n\t\t\t\t}\n\t\t]\n\t},\n\t{\n\t\taddItemsSold:function (addUp:number){\n\t\t\tthis.attributes[0].itemsSold += addUp;\n\t\t},\n\t\tname:'Frootloops',\n\t\tattributes:[\n\t\t\t\t{\n\t\t\t\t\tindex:1,\n\t\t\t\t\titemsSold:10,\n\t\t\t\t}\n\t\t]\n\t},\n\t{\n\t\taddItemsSold:function (addUp:number){\n\t\t\tthis.attributes[0].itemsSold += addUp;\n\t\t},\n\t\tname:'Cornpops',\n\t\tattributes:[\n\t\t\t\t{\n\t\t\t\t\tindex:2,\n\t\t\t\t\titemsSold:1000,\n\t\t\t\t}\n\t\t]\n\t}\n];", + "result":"function cerealsTable():{headers:React.JSX.Element[], content:React.JSX.Element[]}{\n\tconst headers = cerealsObj.map((cereal,key) => {\n\t\treturn {cereal.name}\n\t});\n\n\tconst content = cerealsObj.map((cereal, key)=>{\n\t\treturn(\n\t\t\t\n\t\t\t\t{Object.keys(cereal.attributes[0])[0]}\n\t\t\t\t{Object.keys(cereal.attributes[0])[1]}\n\t\t\t\t \n\t\t\t\n\t\t)\n\t});\n\n\treturn {headers, content};\n}" + } + ] +} \ No newline at end of file diff --git a/src/app/globals.css b/src/app/globals.css index 63c04d6..6a93aa1 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -29,3 +29,8 @@ body { -ms-overflow-style: none; scrollbar-width: none; } + +button{ + border-width: 1px; + border-color: rgb(62, 71, 80); +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 7ee9baa..7263174 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -12,7 +12,7 @@ export const metadata: Metadata = { const RootLayout = ({ children }: { children: React.ReactNode }) => ( - + {children} diff --git a/src/app/page.tsx b/src/app/page.tsx index 9ccfe6d..b5d8476 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -5,6 +5,7 @@ import Examples from './components/Examples'; import Container from './components/Container'; import insertExamples from './promptTemplates'; import Inputs from './components/Inputs'; +import ToolBar from './components/ToolBar'; import { ShotsTypes } from '@/store/inputStore'; const openai = new OpenAI({ @@ -42,20 +43,19 @@ const Home = () => { } return ( - <> -
-
-

ModGen.js

-
-
- - - - - -
+
+
+

ModGen.js

+
- +
+ + + + + +
+
); }; diff --git a/src/store/inputStore.ts b/src/store/inputStore.ts index 8141939..6b3aef7 100644 --- a/src/store/inputStore.ts +++ b/src/store/inputStore.ts @@ -1,4 +1,6 @@ import { create } from 'zustand'; +import defaultExamples from '../app/examples.json'; + export interface ShotsTypes { input: string; result: string; @@ -12,61 +14,20 @@ interface ArrInterface { userResults: string; updateUserInput: (input: string) => void; updateUserResults: (input: string) => void; + loadDefaultExample: () => void; } -const exampleOne: ShotsTypes = { - input: `const rows = [ - { id: 1, lastName: 'Snow', firstName: 'Jon', age: 35 }, - { id: 2, lastName: 'Lannister', firstName: 'Cersei', age: 42 }, - { id: 3, lastName: 'Lannister', firstName: 'Jaime', age: 45 }, - { id: 4, lastName: 'Stark', firstName: 'Arya', age: 16 }, - { id: 5, lastName: 'Targaryen', firstName: 'Daenerys', age: null }, - { id: 6, lastName: 'Melisandre', firstName: null, age: 150 }, - { id: 7, lastName: 'Clifford', firstName: 'Ferrara', age: 44 }, - { id: 8, lastName: 'Frances', firstName: 'Rossini', age: 36 }, - { id: 9, lastName: 'Roxie', firstName: 'Harvey', age: 65 }, - ];`, - result: - `const columns: GridColDef[] = [ - { field: 'id', headerName: 'ID', width: 90 }, - { - field: 'firstName', - headerName: 'First name', - width: 150, - editable: true, - }, - { - field: 'lastName', - headerName: 'Last name', - width: 150, - editable: true, - }, - { - field: 'age', - headerName: 'Age', - type: 'number', - width: 110, - editable: true, - }, - { - field: 'fullName', - headerName: 'Full name', - description: 'This column has a value getter and is not sortable.', - sortable: false, - width: 160, - valueGetter: (params: GridValueGetterParams) => - ` + - "\t`${params.row.firstName || ''} ${params.row.lastName || ''}`" + - `, - }, - ];`, +const exampleOne = (): ShotsTypes => { + const rand = Math.floor(Math.random() * 3); + const sending: ShotsTypes = defaultExamples.examples[rand]; + return sending; }; // array that stores user's inputs and results. // these are the examples that are fed to GPT. export const userInputsArr = create((set) => ({ // combined input and result: example - examples: [exampleOne], + examples: [defaultExamples.examples[0]], updateUserInputArr: (input: ShotsTypes) => set((status: ArrInterface) => ({ examples: [...status.examples, { input: input.input, result: input.result }], @@ -83,4 +44,5 @@ export const userInputsArr = create((set) => ({ set(() => ({ userResults: result, })), + loadDefaultExample: () => set({ examples: [exampleOne()] }), }));