|
1 | 1 | 'use client'
|
2 | 2 |
|
3 |
| -import React, { useState } from 'react' |
| 3 | +import React, { useEffect } from 'react' |
4 | 4 | import { useDispatch, useSelector } from 'react-redux'
|
5 |
| -import { addToolAsync, selectWalletAddress, setError, startFileUpload, endFileUpload } from '@/lib/redux' |
| 5 | +import { useRouter } from 'next/navigation' |
6 | 6 | import {
|
7 |
| - selectToolError, |
8 |
| - selectToolIsLoading, |
9 |
| - selectToolIsUploaded |
10 |
| -} from '@/lib/redux/slices/toolAddSlice/selectors' |
11 |
| -import TextField from '@mui/material/TextField' |
| 7 | + AppDispatch, |
| 8 | + setAddToolError, |
| 9 | + setAddToolLoading, |
| 10 | + setAddToolJson, |
| 11 | + setAddToolSuccess, |
| 12 | + selectWalletAddress, |
| 13 | + selectAddToolLoading, |
| 14 | + selectAddToolError, |
| 15 | + selectAddToolJson, |
| 16 | + selectAddToolSuccess, |
| 17 | + createToolThunk, |
| 18 | +} from '@/lib/redux' |
12 | 19 | import Button from '@mui/material/Button'
|
13 | 20 | import Box from '@mui/material/Box'
|
14 | 21 | import Grid from '@mui/material/Grid'
|
15 |
| -// import { useRouter } from 'next/router' |
| 22 | +import Alert from '@mui/material/Alert' |
| 23 | +import Typography from '@mui/material/Typography' |
| 24 | +import { JsonInput } from '@mantine/core' |
| 25 | +import { MantineProvider } from '@mantine/core'; |
| 26 | + |
16 | 27 |
|
17 | 28 | export default function AddTool() {
|
18 |
| - const dispatch = useDispatch() |
19 |
| - // const router = useRouter() |
| 29 | + const dispatch = useDispatch<AppDispatch>() |
| 30 | + const router = useRouter() |
20 | 31 |
|
21 |
| - const isLoading = useSelector(selectToolIsLoading); |
22 |
| - const error = useSelector(selectToolError); |
23 |
| - const isUploaded = useSelector(selectToolIsUploaded); |
24 | 32 | const walletAddress = useSelector(selectWalletAddress)
|
25 |
| - |
26 |
| - const sampleToolConfig = { |
27 |
| - "class": "CommandLineTool", |
28 |
| - "name": "equibind", |
29 |
| - "description": "Docking of small molecules to a protein", |
30 |
| - "author": "@misc{stärk2022equibind,\n title={EquiBind: Geometric Deep Learning for Drug Binding Structure Prediction}, \n author={Hannes Stärk and Octavian-Eugen Ganea and Lagnajit Pattanaik and Regina Barzilay and Tommi Jaakkola},\n year={2022},\n eprint={2202.05146},\n archivePrefix={arXiv},\n primaryClass={q-bio.BM}\n}", |
31 |
| - "baseCommand": ["/bin/bash", "-c"], |
32 |
| - "arguments": [ |
33 |
| - "mkdir -p /tmp-inputs/tmp;", |
34 |
| - "mkdir -p /tmp-outputs/tmp;", |
35 |
| - "cp /inputs/* /tmp-inputs/tmp/;", |
36 |
| - "ls /tmp-inputs/tmp;", |
37 |
| - "cd /src && python /src/inference.py --config=/src/configs_clean/bacalhau.yml;", |
38 |
| - "mv /tmp-outputs/tmp/* /outputs/;", |
39 |
| - "mv /outputs/lig_equibind_corrected.sdf /outputs/$(inputs.protein.basename)_$(inputs.small_molecule.basename)_docked.$(inputs.small_molecule.ext);", |
40 |
| - "mv /tmp-inputs/tmp/*.pdb /outputs/;"], |
41 |
| - "dockerPull": "ghcr.io/labdao/equibind:main@sha256:21a381d9ab1ff047565685044569c8536a55e489c9531326498b28d6b3cc244f", |
42 |
| - "gpuBool": false, |
43 |
| - "networkBool": false, |
44 |
| - "inputs": { |
45 |
| - "protein": { |
46 |
| - "type": "File", |
47 |
| - "item": "", |
48 |
| - "glob": ["*.pdb"] |
49 |
| - }, |
50 |
| - "small_molecule": { |
51 |
| - "type": "File", |
52 |
| - "item": "", |
53 |
| - "glob": ["*.sdf", "*.mol2"] |
54 |
| - } |
55 |
| - }, |
56 |
| - "outputs": { |
57 |
| - "best_docked_small_molecule": { |
58 |
| - "type": "File", |
59 |
| - "item": "", |
60 |
| - "glob": ["*_docked.sdf", "*_docked.mol2"] |
61 |
| - }, |
62 |
| - "protein": { |
63 |
| - "type": "File", |
64 |
| - "item": "", |
65 |
| - "glob": ["*.pdb"] |
66 |
| - } |
| 33 | + const loading = useSelector(selectAddToolLoading) |
| 34 | + const error = useSelector(selectAddToolError) |
| 35 | + const toolJson = useSelector(selectAddToolJson) |
| 36 | + const toolSuccess = useSelector(selectAddToolSuccess) |
| 37 | + |
| 38 | + useEffect(() => { |
| 39 | + if (toolSuccess) { |
| 40 | + setAddToolSuccess(false) |
| 41 | + dispatch(setAddToolJson("")) |
| 42 | + router.push('/tool/list') |
67 | 43 | }
|
68 |
| - } |
69 |
| - |
70 |
| - const [toolClass, setToolClass] = useState(sampleToolConfig.class) |
71 |
| - const [name, setName] = useState(sampleToolConfig.name); |
72 |
| - const [description, setDescription] = useState(sampleToolConfig.description); |
73 |
| - const [author, setAuthor] = useState(sampleToolConfig.author); |
74 |
| - const [baseCommand, setBaseCommand] = useState(sampleToolConfig.baseCommand); |
75 |
| - const [toolArguments, setToolArguments] = useState(sampleToolConfig.arguments); |
76 |
| - const [dockerPull, setDockerPull] = useState(sampleToolConfig.dockerPull); |
77 |
| - const [gpuBool, setGpuBool] = useState(sampleToolConfig.gpuBool); |
78 |
| - const [networkBool, setNetworkBool] = useState(sampleToolConfig.networkBool); |
79 |
| - const [inputs, setInputs] = useState(sampleToolConfig.inputs); |
80 |
| - const [outputs, setOutputs] = useState(sampleToolConfig.outputs); |
81 |
| - |
82 |
| - const handleToolClassChange = (e: React.ChangeEvent<HTMLInputElement>) => { |
83 |
| - setToolClass(e.target.value); |
84 |
| - }; |
85 |
| - |
86 |
| - const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => { |
87 |
| - setName(e.target.value); |
88 |
| - }; |
89 |
| - |
90 |
| - const handleDescriptionChange = (e: React.ChangeEvent<HTMLInputElement>) => { |
91 |
| - setDescription(e.target.value); |
92 |
| - }; |
93 |
| - |
94 |
| - const handleAuthorChange = (e: React.ChangeEvent<HTMLInputElement>) => { |
95 |
| - setAuthor(e.target.value); |
96 |
| - }; |
97 |
| - |
98 |
| - const handleBaseCommandChange = (e: React.ChangeEvent<HTMLInputElement>) => { |
99 |
| - setBaseCommand(e.target.value.split(',')); |
100 |
| - }; |
101 |
| - |
102 |
| - const handleToolArgumentsChange = (e: React.ChangeEvent<HTMLInputElement>) => { |
103 |
| - setToolArguments(e.target.value.split(',')); |
104 |
| - }; |
| 44 | + }, [toolSuccess, dispatch]) |
105 | 45 |
|
106 |
| - const handleDockerPullChange = (e: React.ChangeEvent<HTMLInputElement>) => { |
107 |
| - setDockerPull(e.target.value); |
108 |
| - }; |
109 |
| - |
110 |
| - const handleGpuBoolChange = (e: React.ChangeEvent<HTMLInputElement>) => { |
111 |
| - setGpuBool(e.target.checked); |
112 |
| - }; |
113 |
| - |
114 |
| - const handleNetworkBoolChange = (e: React.ChangeEvent<HTMLInputElement>) => { |
115 |
| - setNetworkBool(e.target.checked); |
116 |
| - }; |
117 |
| - |
118 |
| - const handleInputsChange = (e: React.ChangeEvent<HTMLInputElement>) => { |
119 |
| - try { |
120 |
| - const value = JSON.parse(e.target.value); |
121 |
| - setInputs(value); |
122 |
| - } catch (error) { |
123 |
| - console.error(error); |
124 |
| - } |
125 |
| - }; |
| 46 | + const handleToolJsonChange = (toolJsonInput: string) => { |
| 47 | + dispatch(setAddToolJson(toolJsonInput)) |
| 48 | + } |
126 | 49 |
|
127 |
| - const handleOutputsChange = (e: React.ChangeEvent<HTMLInputElement>) => { |
| 50 | + const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { |
| 51 | + e.preventDefault(); |
| 52 | + console.log("Submitting tool.json: ", toolJson); |
| 53 | + dispatch(setAddToolLoading(true)) |
| 54 | + dispatch(setAddToolError("")) |
128 | 55 | try {
|
129 |
| - const value = JSON.parse(e.target.value); |
130 |
| - setOutputs(value); |
| 56 | + const toolJsonParsed = JSON.parse(toolJson) |
| 57 | + await dispatch(createToolThunk({ walletAddress, toolJson: toolJsonParsed })) |
131 | 58 | } catch (error) {
|
132 |
| - console.error(error); |
| 59 | + console.error("Error creating tool", error) |
| 60 | + dispatch(setAddToolError("Error creating tool")) |
133 | 61 | }
|
134 |
| - }; |
135 |
| - |
136 |
| - const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { |
137 |
| - e.preventDefault(); |
138 |
| - |
139 |
| - console.log("Submitting tool config") |
140 |
| - console.log("Wallet address: ", walletAddress) |
141 |
| - |
142 |
| - const toolConfig = { |
143 |
| - "name": name, |
144 |
| - "description": description, |
145 |
| - "author": author, |
146 |
| - "baseCommand": baseCommand, |
147 |
| - "arguments": toolArguments, |
148 |
| - "dockerPull": dockerPull, |
149 |
| - "gpuBool": gpuBool, |
150 |
| - "networkBool": networkBool, |
151 |
| - "inputs": inputs, |
152 |
| - "outputs": outputs |
153 |
| - }; |
154 |
| - |
155 |
| - // @ts-ignore |
156 |
| - dispatch(addToolAsync({ toolData: toolConfig, walletAddress })); |
157 |
| - // router.push('/tool/list'); |
158 |
| - }; |
| 62 | + dispatch(setAddToolLoading(false)) |
| 63 | + } |
159 | 64 |
|
160 | 65 | return (
|
161 | 66 | <form onSubmit={handleSubmit}>
|
162 | 67 | <Box maxWidth={500} margin="auto">
|
163 |
| - <Grid container direction="column" spacing={2}> |
164 |
| - <Grid item> |
165 |
| - <TextField |
166 |
| - fullWidth |
167 |
| - label="Tool Class" |
168 |
| - variant="outlined" |
169 |
| - value={toolClass} |
170 |
| - onChange={handleToolClassChange} |
171 |
| - /> |
172 |
| - </Grid> |
173 |
| - <Grid item> |
174 |
| - <TextField |
175 |
| - fullWidth |
176 |
| - label="Tool Name" |
177 |
| - variant="outlined" |
178 |
| - value={name} |
179 |
| - onChange={handleNameChange} |
180 |
| - /> |
181 |
| - </Grid> |
182 |
| - <Grid item> |
183 |
| - <TextField |
184 |
| - fullWidth |
185 |
| - label="Description" |
186 |
| - variant="outlined" |
187 |
| - value={description} |
188 |
| - onChange={handleDescriptionChange} |
189 |
| - /> |
190 |
| - </Grid> |
191 |
| - <Grid item> |
192 |
| - <TextField |
193 |
| - fullWidth |
194 |
| - label="Author" |
195 |
| - variant="outlined" |
196 |
| - value={author} |
197 |
| - onChange={handleAuthorChange} |
198 |
| - /> |
199 |
| - </Grid> |
200 |
| - <Grid item> |
201 |
| - <TextField |
202 |
| - fullWidth |
203 |
| - label="Base Command" |
204 |
| - variant="outlined" |
205 |
| - value={baseCommand} |
206 |
| - onChange={handleBaseCommandChange} |
207 |
| - /> |
208 |
| - </Grid> |
209 |
| - <Grid item> |
210 |
| - <TextField |
211 |
| - fullWidth |
212 |
| - label="Arguments" |
213 |
| - variant="outlined" |
214 |
| - value={toolArguments} |
215 |
| - onChange={handleToolArgumentsChange} |
216 |
| - /> |
217 |
| - </Grid> |
218 |
| - <Grid item> |
219 |
| - <TextField |
220 |
| - fullWidth |
221 |
| - label="Docker Pull" |
222 |
| - variant="outlined" |
223 |
| - value={dockerPull} |
224 |
| - onChange={handleDockerPullChange} |
225 |
| - /> |
226 |
| - </Grid> |
227 |
| - <Grid item> |
228 |
| - <TextField |
229 |
| - fullWidth |
230 |
| - label="GPU Bool" |
231 |
| - variant="outlined" |
232 |
| - value={gpuBool} |
233 |
| - onChange={handleGpuBoolChange} |
234 |
| - /> |
235 |
| - </Grid> |
236 |
| - <Grid item> |
237 |
| - <TextField |
238 |
| - fullWidth |
239 |
| - label="Network Bool" |
240 |
| - variant="outlined" |
241 |
| - value={networkBool} |
242 |
| - onChange={handleNetworkBoolChange} |
243 |
| - /> |
244 |
| - </Grid> |
245 |
| - <Grid item> |
246 |
| - <TextField |
247 |
| - fullWidth |
248 |
| - label="Inputs" |
249 |
| - variant="outlined" |
250 |
| - value={inputs} |
251 |
| - onChange={handleInputsChange} |
252 |
| - /> |
253 |
| - </Grid> |
254 |
| - <Grid item> |
255 |
| - <TextField |
256 |
| - fullWidth |
257 |
| - label="Outputs" |
258 |
| - variant="outlined" |
259 |
| - value={outputs} |
260 |
| - onChange={handleOutputsChange} |
261 |
| - /> |
| 68 | + <Grid container direction="column" spacing={2} justifyContent="center" alignItems="center"> |
| 69 | + <Grid style={{ width: '100%' }} item> |
| 70 | + <MantineProvider> |
| 71 | + <JsonInput |
| 72 | + label="Tool Definition" |
| 73 | + placeholder="Paste your tool's JSON definition here." |
| 74 | + validationError="Invalid JSON" |
| 75 | + autosize |
| 76 | + minRows={10} |
| 77 | + value={toolJson} |
| 78 | + onChange={handleToolJsonChange} |
| 79 | + styles={{ |
| 80 | + input: { 'width': '100%' }, |
| 81 | + }} |
| 82 | + /> |
| 83 | + </MantineProvider> |
262 | 84 | </Grid>
|
| 85 | + {error && ( |
| 86 | + <Box my={2}> |
| 87 | + <Alert severity="error" variant="filled"> |
| 88 | + <Typography align="center">{error}</Typography> |
| 89 | + </Alert> |
| 90 | + </Box> |
| 91 | + )} |
263 | 92 | <Grid item container justifyContent="center">
|
264 | 93 | <Button variant="contained" color="primary" type="submit">
|
265 |
| - {isLoading ? "Submitting..." : "Submit"} |
| 94 | + {loading ? "Submitting..." : "Submit"} |
266 | 95 | </Button>
|
267 | 96 | </Grid>
|
268 | 97 | </Grid>
|
|
0 commit comments