Skip to content

Commit 7fe7578

Browse files
committed
feat: Text Editor
fix: Switching and submitting does not clear the data in the editor chore: Component Splitting
1 parent 56bb648 commit 7fe7578

File tree

16 files changed

+2573
-1014
lines changed

16 files changed

+2573
-1014
lines changed

packages/client/src/App.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { createTheme, CssBaseline, ThemeProvider } from '@mui/material'
22
import Dashboard from './dashboard'
3+
import KeyContext from './ctx/key'
4+
import { useState } from 'react'
35

46
const darkTheme = createTheme({
57
palette: {
@@ -12,10 +14,15 @@ const darkTheme = createTheme({
1214
})
1315

1416
function App() {
17+
const [apiKey, setApiKey] = useState(
18+
localStorage.getItem('mockServerKey') || '',
19+
)
1520
return (
1621
<ThemeProvider theme={darkTheme}>
17-
<CssBaseline />
18-
<Dashboard />
22+
<KeyContext.Provider value={{ apiKey, setApiKey }}>
23+
<CssBaseline />
24+
<Dashboard />
25+
</KeyContext.Provider>
1926
</ThemeProvider>
2027
)
2128
}

packages/client/src/components/ApiEditor.tsx renamed to packages/client/src/components/api-editor.tsx

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
InputLabel,
1212
SelectChangeEvent,
1313
} from '@mui/material'
14-
import JsonEditor, { JsonEditorRef, JsonNode } from './response/JsonEditor'
14+
import JsonEditor, { JsonEditorRef, JsonNode } from './response/json-editor'
1515
import { toast } from 'react-hot-toast'
1616
import i18n from '../utils/i18n'
1717
import { useTranslation } from 'react-i18next'
@@ -74,6 +74,7 @@ function ApiEditor({ type, apiId, onSave, apiKey }: ApiEditorProps) {
7474
resBody: [],
7575
resBodyText: '',
7676
})
77+
resBodyRef.current?.clearData()
7778
}
7879
}, [type, apiId])
7980

@@ -111,6 +112,18 @@ function ApiEditor({ type, apiId, onSave, apiKey }: ApiEditorProps) {
111112
event.preventDefault()
112113
apiData.resBody = resBodyRef.current?.getData() || []
113114
onSave(apiData).then(() => {
115+
setApiData({
116+
name: '',
117+
path: '',
118+
method: 'GET',
119+
description: '',
120+
reqParams: '',
121+
reqHeaders: '',
122+
resHeaders: '',
123+
resResponseType: 'json',
124+
resBody: [],
125+
resBodyText: '',
126+
})
114127
resBodyRef.current?.clearData()
115128
})
116129
}
@@ -185,37 +198,43 @@ function ApiEditor({ type, apiId, onSave, apiKey }: ApiEditorProps) {
185198
multiline
186199
rows={4}
187200
/>
188-
<Typography variant="h6" className='mb-1'>{t('api-editor.response-data')}</Typography>
189-
<div className='flex items-center gap-2'>
190-
<Select
191-
size="small"
192-
labelId="editor-type-label"
193-
id="editor-type-select"
194-
value={apiData.resResponseType}
195-
label={t('api-editor.editor-type')}
196-
onChange={e => setApiData({
197-
...apiData,
198-
resResponseType: e.target.value
199-
})}
200-
>
201+
<Typography variant="h6" className="mb-1">
202+
{t('api-editor.response-data')}
203+
</Typography>
204+
<div className="flex items-center gap-2">
205+
<Select
206+
size="small"
207+
labelId="editor-type-label"
208+
id="editor-type-select"
209+
value={apiData.resResponseType}
210+
label={t('api-editor.editor-type')}
211+
onChange={(e) =>
212+
setApiData({
213+
...apiData,
214+
resResponseType: e.target.value,
215+
})
216+
}
217+
>
201218
<MenuItem value="json">{t('api-editor.structured-editor')}</MenuItem>
202219
<MenuItem value="json-text">{t('api-editor.text-editor')}</MenuItem>
203220
</Select>
204-
<HelpButton helpKey='api-editor.help' />
221+
<HelpButton helpKey="api-editor.help" />
205222
</div>
206-
{
207-
apiData.resResponseType === 'json'
208-
? <JsonEditor initData={apiData.resBody as JsonNode[]} ref={resBodyRef} />
209-
: <ReactShikiEditor
210-
language='json'
211-
theme='dark'
223+
{apiData.resResponseType === 'json' ? (
224+
<JsonEditor initData={apiData.resBody as JsonNode[]} ref={resBodyRef} />
225+
) : (
226+
<ReactShikiEditor
227+
language="json"
228+
theme="dark"
212229
value={apiData.resBodyText}
213-
onChange={value => setApiData({
214-
...apiData,
215-
resBodyText: value
216-
})}
217-
/>
218-
}
230+
onChange={(value) =>
231+
setApiData({
232+
...apiData,
233+
resBodyText: value,
234+
})
235+
}
236+
/>
237+
)}
219238
<Box sx={{ display: 'flex', justifyContent: 'flex-end', gap: 2 }}>
220239
<Button variant="contained" type="submit">
221240
{t(`api-editor.${type === 'update' ? 'update' : 'create'}`)}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { IconButton } from '@mui/material'
2+
import SettingsIcon from '@mui/icons-material/Settings'
3+
import TranslateIcon from '@mui/icons-material/Translate'
4+
import GitHubIcon from '@mui/icons-material/GitHub'
5+
import i18n from '../utils/i18n'
6+
7+
interface HeaderProps {
8+
openKeyDialog: () => void
9+
}
10+
11+
function Header({ openKeyDialog }: HeaderProps) {
12+
const toggleLanguage = () => {
13+
const newLang = i18n.language === 'en' ? 'zh' : 'en'
14+
i18n.changeLanguage(newLang)
15+
}
16+
return (
17+
<div className="w-full h-8 mb-4 z-10 flex items-center justify-end">
18+
<IconButton color="inherit" onClick={openKeyDialog}>
19+
<SettingsIcon />
20+
</IconButton>
21+
<IconButton onClick={toggleLanguage} color="inherit">
22+
<TranslateIcon />
23+
</IconButton>
24+
<IconButton
25+
href="https://github.com/ray-d-song/faker-server"
26+
target="_blank"
27+
rel="noopener noreferrer"
28+
color="inherit"
29+
>
30+
<GitHubIcon />
31+
</IconButton>
32+
</div>
33+
)
34+
}
35+
36+
export default Header
Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import HelpIcon from '@mui/icons-material/Help';
2-
import { Dialog } from '@mui/material';
3-
import Button from '@mui/material/Button';
4-
import { useState } from 'react';
5-
import { useTranslation } from 'react-i18next';
1+
import HelpIcon from '@mui/icons-material/Help'
2+
import { Dialog } from '@mui/material'
3+
import Button from '@mui/material/Button'
4+
import { useState } from 'react'
5+
import { useTranslation } from 'react-i18next'
66

77
interface HelpButtonProps {
88
// i18n key
@@ -15,18 +15,21 @@ function HelpButton({ helpKey }: HelpButtonProps) {
1515

1616
return (
1717
<>
18-
<Button size='small' variant='text' color='primary' onClick={() => {
19-
setOpen(true)
20-
}}>
18+
<Button
19+
size="small"
20+
variant="text"
21+
color="primary"
22+
onClick={() => {
23+
setOpen(true)
24+
}}
25+
>
2126
<HelpIcon />
2227
</Button>
2328
<Dialog open={open} onClose={() => setOpen(false)}>
24-
<pre className='p-2'>
25-
{t(helpKey)}
26-
</pre>
29+
<pre className="p-2">{t(helpKey)}</pre>
2730
</Dialog>
2831
</>
2932
)
3033
}
3134

32-
export default HelpButton
35+
export default HelpButton

packages/client/src/components/request/HeadersGrid.tsx renamed to packages/client/src/components/request/headers-grid.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import CommonGrid from './CommonGrid'
1+
import CommonGrid from './common-grid'
22

33
// 常见的 HTTP Headers
44
const commonHeaders = [

packages/client/src/components/request/QueryGrid.tsx renamed to packages/client/src/components/request/query-grid.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import CommonGrid from './CommonGrid'
1+
import CommonGrid from './common-grid'
22

33
const typeOptions = ['string', 'boolean', 'number', 'array']
44

packages/client/src/components/response/JsonEditor.tsx renamed to packages/client/src/components/response/json-editor.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, memo, useImperativeHandle, forwardRef } from 'react'
1+
import { useState, memo, useImperativeHandle, forwardRef } from 'react'
22
import { SimpleTreeView, TreeItem } from '@mui/x-tree-view'
33
import {
44
TextField,
@@ -10,7 +10,7 @@ import {
1010
} from '@mui/material'
1111
import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline'
1212
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'
13-
import FakerSelect from '../FakerSelect'
13+
import FakerSelect from '../faker-select'
1414
import { useTranslation } from 'react-i18next'
1515

1616
export interface JsonNode {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import {
2+
Dialog,
3+
DialogTitle,
4+
DialogContent,
5+
TextField,
6+
DialogActions,
7+
Button,
8+
} from '@mui/material'
9+
import { t } from 'i18next'
10+
import { useContext } from 'react'
11+
import KeyContext from '../ctx/key'
12+
13+
interface SetKeyDialogProps {
14+
visible: boolean
15+
setVisible: (visible: boolean) => void
16+
onSubmit: () => void
17+
}
18+
19+
function SetKeyDialog({ visible, setVisible, onSubmit }: SetKeyDialogProps) {
20+
const { apiKey, setApiKey } = useContext(KeyContext)
21+
const handleKeySubmit = () => {
22+
localStorage.setItem('mockServerKey', apiKey)
23+
setVisible(false)
24+
onSubmit()
25+
}
26+
return (
27+
<Dialog open={visible} onClose={() => setVisible(false)}>
28+
<DialogTitle>{t('dashboard.input-key')}</DialogTitle>
29+
<DialogContent>
30+
<TextField
31+
autoFocus
32+
margin="dense"
33+
label={t('dashboard.key')}
34+
type="password"
35+
fullWidth
36+
variant="outlined"
37+
value={apiKey}
38+
onChange={(e) => setApiKey(e.target.value)}
39+
/>
40+
</DialogContent>
41+
<DialogActions>
42+
<Button onClick={() => setVisible(false)}>{t('global.cancel')}</Button>
43+
<Button onClick={handleKeySubmit}>{t('global.confirm')}</Button>
44+
</DialogActions>
45+
</Dialog>
46+
)
47+
}
48+
49+
export default SetKeyDialog

0 commit comments

Comments
 (0)