@ant-design/icons#MinusCircleOutlined JavaScript Examples
The following examples show how to use
@ant-design/icons#MinusCircleOutlined.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: edit.js From FP with MIT License | 6 votes |
MutiFormBox = (props) => {
const { option, value, onChange } = props;
const [opts, setOpts] = useState(option || []);
const tpl = [...value];
const addRow = () => {
let base = {label: '', value: ''}
setOpts(prev => [...prev, base])
onChange([...tpl, base])
}
const delRow = (index) => {
opts.splice(index, 1)
setOpts([...opts])
}
const handleChange = (index, e) => {
// console.log(index, e.target.value)
tpl[index].label = tpl[index].value = e.target.value
onChange(tpl)
}
return <div className={styles.mutiRow}>
{
opts.map((item, i) => {
return <div key={i} className={styles.mutiItem}>
<span className={styles.label}>{`选项${i}:`}</span>
<div className={styles.formItem}><Input defaultValue={value[i] ? value[i].label : ''} onChange={handleChange.bind(this, i)} /><MinusCircleOutlined onClick={delRow.bind(this, i)} /></div>
</div>
})
}
<Button type="primary" onClick={addRow} block>
<PlusOutlined />
</Button>
</div>
}
Example #2
Source File: baseForm.js From FP with MIT License | 6 votes |
BaseFormEl = (props) => {
const {isEdit, onEdit, onDel, onAdd} = props
const handleEdit = (v) => {
onEdit && onEdit(v)
}
return <div className={styles.formControl} style={{border: `1px solid ${isEdit ? 'red' : 'transparent'}`}}>
<div className={styles.formItem}>{ props.children }</div>
<div className={styles.actionBar}>
<span className={styles.actionItem} onClick={onDel}><MinusCircleOutlined /></span>
<span className={styles.actionItem} onClick={onAdd}><PlusCircleOutlined /></span>
<span className={styles.actionItem} onClick={handleEdit}><EditOutlined /></span>
</div>
</div>
}
Example #3
Source File: status.jsx From virtuoso-design-system with MIT License | 6 votes |
storiesOf('antd/tag', module).add('status', () =>
<>
<Divider orientation="left">Without icon</Divider>
<div>
<Tag color="success">success</Tag>
<Tag color="processing">processing</Tag>
<Tag color="error">error</Tag>
<Tag color="warning">warning</Tag>
<Tag color="default">default</Tag>
</div>
<Divider orientation="left">With icon</Divider>
<div>
<Tag icon={<CheckCircleOutlined />} color="success">
success
</Tag>
<Tag icon={<SyncOutlined spin />} color="processing">
processing
</Tag>
<Tag icon={<CloseCircleOutlined />} color="error">
error
</Tag>
<Tag icon={<ExclamationCircleOutlined />} color="warning">
warning
</Tag>
<Tag icon={<ClockCircleOutlined />} color="default">
waiting
</Tag>
<Tag icon={<MinusCircleOutlined />} color="default">
stop
</Tag>
</div>
</>,
{ docs: { page: () => (<><h1 id="enus">en-US</h1>
<p>We preset five different colors, you can set color property such as <code>success</code>,<code>processing</code>,<code>error</code>,<code>default</code> and <code>warning</code> to indicate specific status.</p></>) } });
Example #4
Source File: dynamic-form-items-complex.jsx From virtuoso-design-system with MIT License | 5 votes |
Demo = () => {
const [form] = Form.useForm();
const onFinish = values => {
console.log('Received values of form:', values);
};
const handleChange = () => {
form.setFieldsValue({ sights: [] });
};
return (
<Form form={form} name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.Item name="area" label="Area" rules={[{ required: true, message: 'Missing area' }]}>
<Select options={areas} onChange={handleChange} />
</Form.Item>
<Form.List name="sights">
{(fields, { add, remove }) => (
<>
{fields.map(field => (
<Space key={field.key} align="baseline">
<Form.Item
noStyle
shouldUpdate={(prevValues, curValues) =>
prevValues.area !== curValues.area || prevValues.sights !== curValues.sights
}
>
{() => (
<Form.Item
{...field}
label="Sight"
name={[field.name, 'sight']}
fieldKey={[field.fieldKey, 'sight']}
rules={[{ required: true, message: 'Missing sight' }]}
>
<Select disabled={!form.getFieldValue('area')} style={{ width: 130 }}>
{(sights[form.getFieldValue('area')] || []).map(item => (
<Option key={item} value={item}>
{item}
</Option>
))}
</Select>
</Form.Item>
)}
</Form.Item>
<Form.Item
{...field}
label="Price"
name={[field.name, 'price']}
fieldKey={[field.fieldKey, 'price']}
rules={[{ required: true, message: 'Missing price' }]}
>
<Input />
</Form.Item>
<MinusCircleOutlined onClick={() => remove(field.name)} />
</Space>
))}
<Form.Item>
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
Add sights
</Button>
</Form.Item>
</>
)}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
}
Example #5
Source File: dynamic-form-items.jsx From virtuoso-design-system with MIT License | 5 votes |
Demo = () => {
const onFinish = values => {
console.log('Received values of form:', values);
};
return (
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => (
<>
{fields.map(({ key, name, fieldKey, ...restField }) => (
<Space key={key} style={{ display: 'flex', marginBottom: 8 }} align="baseline">
<Form.Item
{...restField}
name={[name, 'first']}
fieldKey={[fieldKey, 'first']}
rules={[{ required: true, message: 'Missing first name' }]}
>
<Input placeholder="First Name" />
</Form.Item>
<Form.Item
{...restField}
name={[name, 'last']}
fieldKey={[fieldKey, 'last']}
rules={[{ required: true, message: 'Missing last name' }]}
>
<Input placeholder="Last Name" />
</Form.Item>
<MinusCircleOutlined onClick={() => remove(name)} />
</Space>
))}
<Form.Item>
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
Add field
</Button>
</Form.Item>
</>
)}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
}
Example #6
Source File: adminChallengeCreate.js From ctf_platform with MIT License | 4 votes |
CreateChallengeForm = (props) => {
const [form] = Form.useForm();
const [editorValue, setEditorValue] = React.useState("")
const [existingCats, setExistingCats] = React.useState([])
const [finalSortedChalls, setfinalSortedChalls] = React.useState([])
useEffect(() => {
var currentValues = form.getFieldsValue()
currentValues.flags = [""]
form.setFieldsValue(currentValues)
//Render existing categories select options
let existCats = []
for (let i = 0; i < props.allCat.length; i++) {
existCats.push(<Option key={props.allCat[i].key} value={props.allCat[i].key}>{props.allCat[i].key}</Option>)
}
setExistingCats(existCats)
//Render existing challenges select options
let existChalls = {}
for (let i = 0; i < props.challenges.length; i++) {
if (!(props.challenges[i].category in existChalls)) existChalls[props.challenges[i].category] = []
existChalls[props.challenges[i].category].push({
value: props.challenges[i]._id,
label: props.challenges[i].name
})
}
let finalChalls = []
for (const category in existChalls) {
finalChalls.push({
value: category,
label: category,
children: existChalls[category]
})
}
setfinalSortedChalls(finalChalls)
}, [])
return (
<Form
form={form}
name="create_challenge_form"
className="create_challenge_form"
onValuesChange={() => { if (props.state.edited === false) props.setState({ edited: true }) }}
onFinish={async (values) => {
props.setState({ edited: false })
if (typeof values.flags === "undefined") {
message.warn("Please enter at least 1 flag")
}
else {
//console.log(values)
props.setState({ loading: true })
if (values.visibility === "false") {
values.visibility = false
}
else {
values.visibility = true
}
if (values.dynamic === "false") {
values.dynamic = false
}
else {
values.dynamic = true
}
if (typeof values.writeup !== "undefined") {
if (typeof values.writeupComplete === "undefined") {
values.writeupComplete = true
}
}
const category = (typeof values.category1 !== "undefined") ? values.category1 : values.category2
let requires = undefined
if (values.requires && values.requires.length > 0) requires = values.requires[1]
await fetch(window.ipAddress + "/v1/challenge/new", {
method: 'post',
headers: { 'Content-Type': 'application/json', "Authorization": window.IRSCTFToken },
body: JSON.stringify({
"name": values.name,
"category": category,
"description": values.description,
"points": values.points,
"flags": values.flags,
"tags": values.tags,
"hints": values.hints,
"max_attempts": values.max_attempts,
"visibility": values.visibility,
"writeup": values.writeup,
"writeupComplete": values.writeupComplete,
"requires": requires,
"dynamic": values.dynamic,
"initial": values.initial,
"minSolves": values.minSolves,
"minimum": values.minimum
})
}).then((results) => {
return results.json(); //return data in JSON (since its JSON data)
}).then((data) => {
//console.log(data)
if (data.success === true) {
message.success({ content: "Created challenge " + values.name + " successfully!" })
form.resetFields()
props.handleCreateBack()
}
else if (data.error === "exists") {
message.warn("A challenge with an existing name exists")
}
else {
message.error({ content: "Oops. Unknown error, please contact an admin." })
}
}).catch((error) => {
console.log(error)
message.error({ content: "Oops. Issue connecting with the server or client error, please check console and report the error. " });
})
props.setState({ loading: false })
}
}}
>
<Prompt
when={props.state.edited}
message='The challenge details you entered have not been saved. Are you sure you want to leave?'
/>
<h1>Challenge Name:</h1>
<Form.Item
name="name"
rules={[{ required: true, message: 'Please enter a challenge name' }]}
>
<Input allowClear placeholder="Challenge name" />
</Form.Item>
<Divider />
<h1>Challenge Category:</h1>
<h4>Select an Existing Category: </h4>
<Form.Item
name="category1"
rules={[{ required: !props.state.selectCatDisabled, message: 'Please enter a challenge category' }]}
>
<Select
disabled={props.state.selectCatDisabled}
allowClear
showSearch
placeholder="Select an existing Category"
onChange={(value) => {
if (value) {
props.setState({ inputCatDisabled: true })
}
else {
props.setState({ inputCatDisabled: false })
}
}}
>
{existingCats}
</Select>
</Form.Item>
<h4>Enter a New Category</h4>
<Form.Item
name="category2"
rules={[{ required: !props.state.inputCatDisabled, message: 'Please enter a challenge category' }]}
>
<Input onChange={(e) => {
e.target.value.length > 0 ? props.setState({ selectCatDisabled: true }) : props.setState({ selectCatDisabled: false })
}} disabled={props.state.inputCatDisabled} allowClear placeholder="Enter a new challenge category" />
</Form.Item>
<Divider />
<Suspense fallback={<div style={{ height: "100%", width: "100%", display: "flex", justifyContent: "center", alignItems: "center", zIndex: 15 }}>
<Ellipsis color="#177ddc" size={120} ></Ellipsis>
</div>}>
<h1>Challenge Description (Supports <a href="https://guides.github.com/features/mastering-markdown/" target="_blank" rel="noreferrer">Markdown</a> and <a href="https://github.com/rehypejs/rehype-raw" target="_blank" rel="noreferrer">HTML</a>):</h1>
<Form.Item
name="description"
rules={[{ required: true, message: 'Please enter a description' }]}
valuePropName={editorValue}
>
<MDEditor value={editorValue} onChange={(value) => { setEditorValue(value) }} preview="edit" />
</Form.Item>
<h3>Challenge Description Preview</h3>
<Card
type="inner"
bordered={true}
bodyStyle={{ backgroundColor: "#262626", textAlign: "center" }}
className="challengeModal"
>
<MarkdownRender>{editorValue}</MarkdownRender>
</Card>
</Suspense>
<Divider />
<div className="settings-responsive2" style={{ display: "flex", justifyContent: "space-around" }}>
<div style={{ display: "flex", flexDirection: "column", justifyContent: "center" }}>
<Card>
<h1>Challenge Points:</h1>
<Form.Item
name="points"
rules={[{ required: true, message: 'Please enter challenge points' }, {
type: 'integer',
message: "Please enter a valid integer between 0-100000",
},]}
initialValue={0}
>
<InputNumber disabled={props.state.dynamic} min={0} max={100000} style={{ width: "30ch" }} ></InputNumber>
</Form.Item>
</Card>
<Card>
<h1>Maximum Number of Attempts (Set to 0 for unlimited)</h1>
<Form.Item
name="max_attempts"
rules={[{ required: true, message: 'Please enter the maximum number of attempts' }, {
type: 'integer',
message: "Please enter a valid integer between 0-10000",
},]}
style={{ alignText: 'center' }}
initialValue={0}
>
<InputNumber min={0} max={10000} style={{ width: "30ch" }}></InputNumber>
</Form.Item>
</Card>
</div>
<Divider type="vertical" style={{ height: "inherit" }} />
<div style={{ display: "flex", flexDirection: "column" }}>
<Form.List name="flags" >
{(fields, { add, remove }) => {
return (
<Card>
<h1>Flags</h1>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="start">
<Form.Item
{...field}
name={[field.name]}
fieldKey={[field.fieldKey]}
rules={[{ required: true, message: 'Missing flag' }, { message: "Please enter a flag that is < 1000 characters", pattern: /^.{1,1000}$/ }]}
>
<Input style={{ width: "50ch" }} placeholder="Flag" />
</Form.Item>
{fields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
style={{ margin: '0 8px', color: "red" }}
onClick={() => {
remove(field.name);
}}
/>
) : null}
</Space>
))}
<Form.Item>
<Button
type="dashed"
onLoad={() => { if (fields.length < 1) add() }}
onClick={() => {
add();
}}
block
style={{ width: "50ch" }}
>
<PlusOutlined /> Add Flag
</Button>
</Form.Item>
</Card>
);
}}
</Form.List>
<Form.List name="tags">
{(fields, { add, remove }) => {
return (
<Card>
<h1>Tags</h1>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="start">
<Form.Item
{...field}
name={[field.name]}
fieldKey={[field.fieldKey]}
rules={[{ required: true, message: 'Missing tag' }]}
>
<Input placeholder="Tag" style={{ width: "50ch" }} />
</Form.Item>
<MinusCircleOutlined
className="dynamic-delete-button"
style={{ margin: '0 8px', color: "red" }}
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
style={{ width: "50ch" }}
>
<PlusOutlined /> Add Tag
</Button>
</Form.Item>
</Card>
);
}}
</Form.List>
</div>
</div>
<Divider />
<div className="settings-responsive2" style={{ display: "flex", justifyContent: "space-around" }}>
<div style={{ display: "flex", flexDirection: "column" }}>
<Form.List name="hints" >
{(fields, { add, remove }) => {
return (
<Card>
<h1>Hints</h1>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="start">
<Form.Item
{...field}
name={[field.name, "hint"]}
fieldKey={[field.fieldKey, "hint"]}
rules={[{ required: true, message: 'Missing hint' }]}
>
<Input placeholder="Hint" style={{ width: "20vw" }} />
</Form.Item>
<Form.Item
{...field}
name={[field.name, "cost"]}
fieldKey={[field.fieldKey, "cost"]}
rules={[{ required: true, message: 'Missing cost for hint' }, {
type: 'integer',
message: "Please enter a valid integer between 0-10000",
},]}
>
<InputNumber min={0} max={10000} placeholder="Cost"></InputNumber>
</Form.Item>
<MinusCircleOutlined
style={{ color: "red" }}
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
style={{ width: "50ch" }}
>
<PlusOutlined /> Add Hint
</Button>
</Form.Item>
</Card>
);
}}
</Form.List>
<Card>
<h1>Writeup Link (Optional)</h1>
<Form.Item
name="writeup"
rules={[
{
type: 'url',
message: "Please enter a valid link",
}]}
>
<Input allowClear style={{ width: "50ch" }} placeholder="Enter a writeup link for this challenge" />
</Form.Item>
<div style={{ display: "flex", alignContent: "center" }}>
<h4 style={{ marginRight: "2ch" }}>Release Writeup Only After Completion: </h4>
<Form.Item
name="writeupComplete"
>
<Switch defaultChecked />
</Form.Item>
</div>
</Card>
<Card>
<h1>Visibility</h1>
<Form.Item
name="visibility"
rules={[{ required: true, message: 'Please set the challenge visibility' }]}
initialValue="false"
>
<Select style={{ width: "20ch" }}>
<Option value="false"><span style={{ color: "#d32029" }}>Hidden <EyeInvisibleOutlined /></span></Option>
<Option value="true"><span style={{ color: "#49aa19" }}>Visible <EyeOutlined /></span></Option>
</Select>
</Form.Item>
</Card>
</div>
<Divider type="vertical" style={{ height: "inherit" }} />
<div style={{ display: "flex", flexDirection: "column" }}>
<Card>
<h1>Challenge Required: </h1>
<Form.Item
name="requires"
>
<Cascader
options={finalSortedChalls}
allowClear
showSearch
placeholder="Select an existing challenge" />
</Form.Item>
<p>Locks this challenge until the provided challenge above has been solved.</p>
</Card>
<Card>
<h1>Dynamic Scoring</h1>
<Form.Item
name="dynamic"
rules={[{ required: true, message: 'Please set whether the challenge uses dynamic scoring' }]}
initialValue="false"
>
<Select onSelect={(option) => { option === "false" ? props.setState({ dynamic: false }) : props.setState({ dynamic: true }) }} style={{ width: "20ch" }}>
<Option value="false"><span style={{ color: "#d32029" }}>Disabled</span></Option>
<Option value="true"><span style={{ color: "#49aa19" }}>Enabled</span></Option>
</Select>
</Form.Item>
<h1>Initial Points:</h1>
<Form.Item
name="initial"
rules={[{ required: props.state.dynamic, message: 'Please enter the initial challenge points' }, {
type: 'integer',
message: "Please enter a valid integer between 1-100000",
},]}
initialValue={500}
>
<InputNumber disabled={!props.state.dynamic} min={1} max={100000} ></InputNumber>
</Form.Item>
<p>Initial number of points when there are 0/1 solves on a challenge</p>
<h1>Minimum Points:</h1>
<Form.Item
name="minimum"
rules={[{ required: props.state.dynamic, message: 'Please enter the minimum challenge points' }, {
type: 'integer',
message: "Please enter a valid integer between 0-100000",
},]}
initialValue={100}
>
<InputNumber disabled={!props.state.dynamic} min={0} max={100000} ></InputNumber>
</Form.Item>
<p>Minimum amount of points that the challenge can decay too</p>
<h1>Solves to Minimum:</h1>
<Form.Item
name="minSolves"
rules={[{ required: props.state.dynamic, message: 'Please enter the solves to minimum' }, {
type: 'integer',
message: "Please enter a valid integer between 1-100000",
},]}
initialValue={50}
>
<InputNumber disabled={!props.state.dynamic} min={1} max={100000} ></InputNumber>
</Form.Item>
<p>Number of solves on the challenge till it decays to the minimum point.</p>
</Card>
</div>
</div>
<Form.Item>
<div style={{ display: "flex", justifyContent: "space-between", flexDirection: "row", marginTop: "2vh" }}>
<div>
<Button style={{ marginBottom: "1.5vh", marginRight: "2vw", backgroundColor: "#d4b106", borderColor: "", color: "white" }} onClick={() => { props.previewChallenge(form.getFieldsValue()); }}>Preview</Button>
<Button loading={props.loadingStatus} type="primary" htmlType="submit" className="login-form-button" style={{ marginBottom: "1.5vh" }}>Create Challenge</Button>
</div>
<div>
<Button style={{ marginRight: "2vw" }} type="primary" danger onClick={() => { form.resetFields() }}>Clear</Button>
</div>
</div>
</Form.Item>
</Form>
);
}
Example #7
Source File: adminChallengeEdit.js From ctf_platform with MIT License | 4 votes |
CreateChallengeForm = (props) => {
const [form] = Form.useForm();
const [editorValue, setEditorValue] = React.useState("")
const [existingCats, setExistingCats] = React.useState([])
const [finalSortedChalls, setFinalSortedChalls] = React.useState([])
//Render existing categories select options
useEffect(() => {
let existingCats = []
for (let i = 0; i < props.allCat.length; i++) {
existingCats.push(<Option key={props.allCat[i].key} value={props.allCat[i].key}>{props.allCat[i].key}</Option>)
}
//Render existing challenges select options minus the challenge itself
let existingChalls = {}
for (let i = 0; i < props.challenges.length; i++) {
if (props.challenges[i].name !== props.initialData.name) {
if (!(props.challenges[i].category in existingChalls)) existingChalls[props.challenges[i].category] = []
existingChalls[props.challenges[i].category].push({
value: props.challenges[i]._id,
label: props.challenges[i].name
})
}
}
setExistingCats(existingCats)
let finalSortedChalls = []
for (const category in existingChalls) {
finalSortedChalls.push({
value: category,
label: category,
children: existingChalls[category]
})
}
setFinalSortedChalls(finalSortedChalls)
let initialData = JSON.parse(JSON.stringify(props.initialData))
if (props.initialData.visibility === false) {
initialData.visibility = "false"
}
else if (props.initialData.visibility === true) {
initialData.visibility = "true"
}
// if we put only props.initialData.requires, we are merely putting a reference to props.initialData.requires, which is this array, creating a "loop"
if (props.initialData.requires) initialData.requires = [props.IDNameMapping[props.initialData.requires], props.initialData.requires]
if (props.initialData.dynamic === false) {
initialData.dynamic = "false"
}
else if (props.initialData.dynamic === true) {
initialData.dynamic = "true"
props.setState({ dynamic: true })
}
else {
initialData.dynamic = "false"
}
initialData.category1 = initialData.category
form.setFieldsValue(initialData)
setEditorValue(initialData.description)
}, [])
return (
<Form
form={form}
name="create_challenge_form"
className="create_challenge_form"
onValuesChange={() => { if (props.state.edited === false) props.setState({ edited: true }) }}
onFinish={async (values) => {
props.setState({ edited: false })
if (typeof values.flags === "undefined") {
message.warn("Please enter at least 1 flag")
}
else {
if (values.visibility === "false") {
values.visibility = false
}
else {
values.visibility = true
}
if (values.dynamic === "false") {
values.dynamic = false
}
else {
values.dynamic = true
}
if (typeof values.writeup !== "undefined") {
if (typeof values.writeupComplete === "undefined") {
values.writeupComplete = true
}
}
const category = (typeof values.category1 !== "undefined") ? values.category1 : values.category2
props.setState({ editLoading: true })
let requires = ""
if (values.requires && values.requires.length > 0) requires = values.requires[1]
await fetch(window.ipAddress + "/v1/challenge/edit", {
method: 'post',
headers: { 'Content-Type': 'application/json', "Authorization": window.IRSCTFToken },
body: JSON.stringify({
"id": props.initialData._id,
"name": values.name,
"category": category,
"description": values.description,
"points": values.points,
"flags": values.flags,
"tags": values.tags,
"hints": values.hints,
"max_attempts": values.max_attempts,
"visibility": values.visibility,
"writeup": values.writeup,
"writeupComplete": values.writeupComplete,
"requires": requires,
"dynamic": values.dynamic,
"initial": values.initial,
"minSolves": values.minSolves,
"minimum": values.minimum
})
}).then((results) => {
return results.json(); //return data in JSON (since its JSON data)
}).then((data) => {
if (data.success === true) {
message.success({ content: "Edited challenge \"" + props.initialData.name + "\" successfully!" })
props.handleEditChallBack()
setEditorValue("")
form.resetFields()
}
else if (data.error === "exists") {
message.warn("A challenge with an existing name exists")
}
else {
message.error({ content: "Oops. Unknown error" })
}
}).catch((error) => {
console.log(error)
message.error({ content: "Oops. There was an issue connecting with the server" });
})
props.setState({ editLoading: false })
}
}}
>
<Prompt
when={props.state.edited}
message='The challenge details you modified have not been saved. Are you sure you want to leave?'
/>
<p><b><u>ID:</u></b> <code>{props.initialData._id}</code></p>
<h1>Challenge Name:</h1>
<Form.Item
name="name"
rules={[{ required: true, message: 'Please enter a challenge name' }]}
>
<Input allowClear placeholder="Challenge name" />
</Form.Item>
<Divider />
<h1>Challenge Category:</h1>
<h4>Select an Existing Category: </h4>
<Form.Item
name="category1"
initialValue={""}
rules={[{ required: !props.state.selectCatDisabled, message: 'Please enter a challenge category' }]}
>
<Select
disabled={props.state.selectCatDisabled}
allowClear
showSearch
placeholder="Select an existing Category"
onChange={(value) => {
if (value) {
props.setState({ inputCatDisabled: true })
}
else {
props.setState({ inputCatDisabled: false })
}
}}
>
{existingCats}
</Select>
</Form.Item>
<h4>Enter a New Category</h4>
<Form.Item
name="category2"
rules={[{ required: !props.state.inputCatDisabled, message: 'Please enter a challenge category' }]}
>
<Input onChange={(e) => {
e.target.value.length > 0 ? props.setState({ selectCatDisabled: true }) : props.setState({ selectCatDisabled: false })
}} disabled={props.state.inputCatDisabled} allowClear placeholder="Enter a new challenge category" />
</Form.Item>
<Divider />
<h1>Challenge Description (Supports <a href="https://guides.github.com/features/mastering-markdown/" target="_blank" rel="noreferrer">Markdown</a> and <a href="https://github.com/rehypejs/rehype-raw" target="_blank" rel="noreferrer">HTML</a>)):</h1>
<Suspense fallback={<div style={{ height: "100%", width: "100%", display: "flex", justifyContent: "center", alignItems: "center", zIndex: 15 }}>
<Ellipsis color="#177ddc" size={120} ></Ellipsis>
</div>}>
<Form.Item
name="description"
rules={[{ required: true, message: 'Please enter a description' }]}
valuePropName={editorValue}
>
<MDEditor value={editorValue} onChange={(value) => { setEditorValue(value) }} preview="edit" />
</Form.Item>
<h3>Challenge Description Preview</h3>
<Card
type="inner"
bordered={true}
bodyStyle={{ backgroundColor: "#262626", textAlign: "center" }}
className="challengeModal"
>
<MarkdownRender>{editorValue}</MarkdownRender>
</Card>
</Suspense>
<Divider />
<div className="settings-responsive2" style={{ display: "flex", justifyContent: "space-around" }}>
<div style={{ display: "flex", flexDirection: "column", justifyContent: "center", alignContent: "center" }}>
<Card className="settings-card">
<h1>Challenge Points:</h1>
<Form.Item
name="points"
rules={[{ required: true, message: 'Please enter challenge points' }, {
type: 'integer',
message: "Please enter a valid integer between 0-100000",
},]}
initialValue={0}
>
<InputNumber disabled={props.state.dynamic} min={0} max={100000} style={{ width: "30ch" }} ></InputNumber>
</Form.Item>
</Card>
<Card className="settings-card">
<h1>Maximum Number of Attempts (Set to 0 for unlimited)</h1>
<Form.Item
name="max_attempts"
rules={[{ required: true, message: 'Please enter the maximum number of attempts' }, {
type: 'integer',
message: "Please enter a valid integer between 0-10000",
},]}
style={{ alignText: 'center' }}
initialValue={0}
>
<InputNumber min={0} max={10000} style={{ width: "30ch" }}></InputNumber>
</Form.Item>
</Card>
</div>
<Divider type="vertical" style={{ height: "inherit" }}></Divider>
<div style={{ display: "flex", flexDirection: "column" }}>
<Form.List name="flags" >
{(fields, { add, remove }) => {
return (
<Card className="settings-card">
<h1>Flags</h1>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="start">
<Form.Item
{...field}
name={[field.name]}
fieldKey={[field.fieldKey]}
rules={[{ required: true, message: 'Missing flag' }, { message: "Please enter a flag that is < 1000 characters", pattern: /^.{1,1000}$/ }]}
>
<Input style={{ width: "50ch" }} placeholder="Flag" />
</Form.Item>
{fields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
style={{ margin: '0 8px', color: "red" }}
onClick={() => {
remove(field.name);
}}
/>
) : null}
</Space>
))}
<Form.Item>
<Button
type="dashed"
onLoad={() => { if (fields.length < 1) add() }}
onClick={() => {
add();
}}
block
style={{ width: "50ch" }}
>
<PlusOutlined /> Add Flag
</Button>
</Form.Item>
</Card>
);
}}
</Form.List>
<Form.List name="tags">
{(fields, { add, remove }) => {
return (
<Card className="settings-card">
<h1>Tags</h1>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="start">
<Form.Item
{...field}
name={[field.name]}
fieldKey={[field.fieldKey]}
rules={[{ required: true, message: 'Missing tag' }]}
>
<Input placeholder="Tag" style={{ width: "50ch" }} />
</Form.Item>
<MinusCircleOutlined
className="dynamic-delete-button"
style={{ margin: '0 8px', color: "red" }}
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
style={{ width: "50ch" }}
>
<PlusOutlined /> Add Tag
</Button>
</Form.Item>
</Card>
);
}}
</Form.List>
</div>
</div>
<Divider />
<div className="settings-responsive2" style={{ display: "flex", justifyContent: "space-around" }}>
<div style={{ display: "flex", flexDirection: "column" }}>
<Form.List name="hints" >
{(fields, { add, remove }) => {
return (
<Card className="settings-card">
<h1>Hints</h1>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="start">
<Form.Item
{...field}
name={[field.name, "hint"]}
fieldKey={[field.fieldKey, "hint"]}
rules={[{ required: true, message: 'Missing hint' }]}
>
<Input placeholder="Hint" style={{ width: "20vw" }} />
</Form.Item>
<Form.Item
{...field}
name={[field.name, "cost"]}
fieldKey={[field.fieldKey, "cost"]}
rules={[{ required: true, message: 'Missing cost for hint' }, {
type: 'integer',
message: "Please enter a valid integer between 0-10000",
},]}
>
<InputNumber min={0} max={10000} placeholder="Cost"></InputNumber>
</Form.Item>
<MinusCircleOutlined
style={{ color: "red" }}
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
style={{ width: "50ch" }}
>
<PlusOutlined /> Add Hint
</Button>
</Form.Item>
</Card>
);
}}
</Form.List>
<Card className="settings-card">
<h1>Writeup Link (Optional)</h1>
<Form.Item
name="writeup"
rules={[
{
type: 'url',
message: "Please enter a valid link",
}]}
>
<Input allowClear style={{ width: "50ch" }} placeholder="Enter a writeup link for this challenge" />
</Form.Item>
<div style={{ display: "flex", alignContent: "center" }}>
<h4 style={{ marginRight: "2ch" }}>Release Writeup Only After Completion: </h4>
<Form.Item
name="writeupComplete"
>
<Switch defaultChecked />
</Form.Item>
</div>
</Card>
<Card className="settings-card">
<h1>Visibility</h1>
<Form.Item
name="visibility"
rules={[{ required: true, message: 'Please set the challenge visibility' }]}
initialValue="false"
>
<Select style={{ width: "20ch" }}>
<Option value="false"><span style={{ color: "#d32029" }}>Hidden <EyeInvisibleOutlined /></span></Option>
<Option value="true"><span style={{ color: "#49aa19" }}>Visible <EyeOutlined /></span></Option>
</Select>
</Form.Item>
</Card>
</div>
<Divider type="vertical" style={{ height: "inherit" }} />
<div style={{ display: "flex", flexDirection: "column" }}>
<Card>
<h1>Challenge Required: </h1>
<Form.Item
name="requires"
>
{/*
The issue with this is that displayRender is supposed to return an array,
but setting a value causes it to become a string and error out
*/}
<Cascader
options={finalSortedChalls}
allowClear
showSearch
placeholder="Select an existing challenge" />
</Form.Item>
<p>Locks this challenge until the provided challenge above has been solved.</p>
</Card>
<Card className="settings-card">
<h1>Dynamic Scoring</h1>
<Form.Item
name="dynamic"
rules={[{ required: props.state.dynamic, message: 'Please set whether the challenge uses dynamic scoring' }]}
initialValue="false"
>
<Select style={{ width: "20ch" }} onSelect={(option) => { option === "false" ? props.setState({ dynamic: false }) : props.setState({ dynamic: true }) }}>
<Option value="false"><span style={{ color: "#d32029" }}>Disabled</span></Option>
<Option value="true"><span style={{ color: "#49aa19" }}>Enabled</span></Option>
</Select>
</Form.Item>
<h1>Initial Points:</h1>
<Form.Item
name="initial"
rules={[{ required: props.state.dynamic, message: 'Please enter the initial challenge points' }, {
type: 'integer',
message: "Please enter a valid integer between 1-100000",
},]}
initialValue={500}
>
<InputNumber disabled={!props.state.dynamic} min={1} max={100000} ></InputNumber>
</Form.Item>
<p>Initial number of points when there are 0/1 solves on a challenge</p>
<h1>Minimum Points:</h1>
<Form.Item
name="minimum"
rules={[{ required: props.state.dynamic, message: 'Please enter the minimum challenge points' }, {
type: 'integer',
message: "Please enter a valid integer between 0-100000",
},]}
initialValue={100}
>
<InputNumber disabled={!props.state.dynamic} min={0} max={100000} ></InputNumber>
</Form.Item>
<p>Minimum amount of points that the challenge can decay too</p>
<h1>Solves to Minimum:</h1>
<Form.Item
name="minSolves"
rules={[{ required: props.state.dynamic, message: 'Please enter the solves to minimum' }, {
type: 'integer',
message: "Please enter a valid integer between 1-100000",
},]}
initialValue={50}
>
<InputNumber disabled={!props.state.dynamic} min={1} max={100000} ></InputNumber>
</Form.Item>
<p>Number of solves on the challenge till it decays to the minimum point.</p>
</Card>
</div>
</div>
<Form.Item>
<div style={{ display: "flex", justifyContent: "space-between", flexDirection: "row", marginTop: "3ch" }}>
<div>
<Button style={{ marginBottom: "1.5vh", marginRight: "2vw", backgroundColor: "#d4b106", borderColor: "", color: "white" }} onClick={() => { props.previewChallenge(form.getFieldsValue()) }}>Preview</Button>
<Button type="primary" htmlType="submit" className="login-form-button" style={{ marginBottom: "1.5vh" }} loading={props.editLoading}>Edit Challenge</Button>
</div>
<div>
<Button style={{ marginRight: "2vw" }} type="primary" danger onClick={() => { form.resetFields() }}>Clear</Button>
</div>
</div>
</Form.Item>
</Form>
);
}
Example #8
Source File: adminUsers.js From ctf_platform with MIT License | 4 votes |
render() {
return (
<Layout style={{ height: "100%", width: "100%", backgroundColor: "rgba(0, 0, 0, 0)" }}>
<Modal
title={<span>Change User Permissions <ClusterOutlined /></span>}
visible={this.state.permissionModal}
onOk={this.changePermissions}
onCancel={() => { this.setState({ permissionModal: false }) }}
confirmLoading={this.state.modalLoading}
>
<Select size="large" value={this.state.permissionChangeTo} style={{ width: "30ch" }} onSelect={(value) => { this.setState({ permissionChangeTo: value }) }}>
<Option value="0">0 - Normal User</Option>
<Option value="1">1 - Challenge Creator User</Option>
<Option value="2">2 - Admin User</Option>
</Select>
<br />
<br />
<ul>
<li><b>0 - Normal User</b>: Has access to the basic functions and nothing else</li>
<li><b>1 - Challenge Creator User</b>: Has the additional power of submitting new challenges, but not modifying existing ones</li>
<li><b>2 - Admin User</b>: Has full access to the platform via the admin panel.</li>
</ul>
</Modal>
<Modal
title="Create New Account"
visible={this.state.createUserModal}
footer={null}
onCancel={() => { this.setState({ createUserModal: false }) }}
>
<RegisterForm createAccount={this.createAccount.bind(this)} setState={this.setState.bind(this)} />
</Modal>
<Modal
title={"Changing Account Password For: " + this.state.username}
visible={this.state.passwordResetModal}
footer={null}
onCancel={() => { this.setState({ passwordResetModal: false }) }}
>
<ChangePasswordForm username={this.state.username} setState={this.setState.bind(this)} />
</Modal>
<Modal
title={"Changing Category For: " + this.state.username}
visible={this.state.categoryChangeModal}
footer={null}
onCancel={() => { this.setState({ categoryChangeModal: false }) }}
>
<SelectParticipantCategoryForm fillTableData={this.fillTableData.bind(this)} categoryList={this.state.categoryList} username={this.state.username} participantCategory={this.state.participantCategory} />
</Modal>
<Modal
title={"Changing Email For: " + this.state.username}
visible={this.state.emailChangeModal}
footer={null}
onCancel={() => { this.setState({ emailChangeModal: false }) }}
>
<ChangeEmailForm fillTableData={this.fillTableData.bind(this)} username={this.state.username} setState={this.setState.bind(this)} />
</Modal>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<div style={{ display: "flex", alignItems: "center", height: "2ch" }}>
<Button type="primary" style={{ marginBottom: "2vh", marginRight: "1ch" }} icon={<UserOutlined />} onClick={() => { this.setState({ createUserModal: true }) }}>Create New User</Button>
{this.state.loading && (
<div style={{ display: "flex", justifyContent: "center", alignItems: "center" }}>
<Ellipsis color="#177ddc" size={60} ></Ellipsis>
<h1>Loading Users</h1>
</div>
)}
</div>
<Button loading={this.state.loading} type="primary" shape="circle" size="large" style={{ marginBottom: "2vh", maxWidth: "25ch" }} icon={<RedoOutlined />} onClick={async () => { await Promise.all([this.fillTableData(), this.getDisableStates()]); message.success("Users list refreshed.") }} />
</div>
<div style={{ display: "flex", alignItems: "center" }}>
<Button disabled={this.state.disableEditButtons} style={{ marginBottom: "2vh", marginRight: "1ch", backgroundColor: "#a61d24" }} icon={<DeleteOutlined />} onClick={() => {
confirm({
confirmLoading: this.state.disableEditButtons,
title: 'Are you sure you want to delete the user(s) (' + this.state.selectedTableKeys.join(", ") + ')? This action is irreversible.',
icon: <ExclamationCircleOutlined />,
onOk: (close) => { this.deleteAccounts(close.bind(this), this.state.selectedTableKeys) },
onCancel: () => { },
});
}}>Delete Users</Button>
<Button type="default" disabled={this.state.disableEditButtons} style={{ marginBottom: "2vh", marginRight: "1ch", backgroundColor: "#6e6e6e" }} icon={<CheckOutlined style={{ color: "#49aa19" }} />} onClick={() => {
confirm({
confirmLoading: this.state.disableEditButtons,
title: 'Are you sure you want to verify the user(s) (' + this.state.selectedTableKeys.join(", ") + ')?',
icon: <ExclamationCircleOutlined />,
onOk: (close) => { this.verifyAccounts(close.bind(this), this.state.selectedTableKeys) },
onCancel: () => { },
});
}}>Verify Users</Button>
<Button type="default" disabled={this.state.disableEditButtons} style={{ marginBottom: "2vh", marginRight: "1ch", backgroundColor: "#6e6e6e" }} icon={<CloseOutlined style={{ color: "#a61d24" }} />} onClick={() => {
confirm({
confirmLoading: this.state.disableEditButtons,
title: 'Are you sure you want to un-verify the user(s) (' + this.state.selectedTableKeys.join(", ") + ')?',
content: 'Please note that this action will send a new email per user asking them to re-verify.',
icon: <ExclamationCircleOutlined />,
onOk: (close) => { this.unverifyAccounts(close.bind(this), this.state.selectedTableKeys) },
onCancel: () => { },
});
}}>Un-Verify Users</Button>
</div>
<Table rowSelection={{ selectedRowKeys: this.state.selectedTableKeys, onChange: this.handleTableSelect.bind(this) }} style={{ overflow: "auto" }} dataSource={this.state.dataSource} locale={{
emptyText: (
<div style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", marginTop: "10vh" }}>
<FileUnknownTwoTone style={{ color: "#177ddc", fontSize: "400%", zIndex: 1 }} />
<h1 style={{ fontSize: "200%" }}>No users found/created</h1>
</div>
)
}}>
<Column title="Username" dataIndex="username" key="username"
render={(text, row, index) => {
return <Link to={"/Profile/" + text}><a style={{ fontWeight: 700 }}>{text}</a></Link>;
}}
filterDropdown={({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div style={{ padding: 8 }}>
<Input
placeholder="Search Username"
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => confirm()}
style={{ marginBottom: 8, display: 'block' }}
autoFocus
/>
<Space>
<Button
type="primary"
onClick={() => { confirm() }}
icon={<SearchOutlined />}
>
Search
</Button>
<Button onClick={() => clearFilters()}>
Reset
</Button>
</Space>
</div>
)}
onFilter={(value, record) => record.username.toLowerCase().trim().includes(value.toLowerCase())}
filterIcon={filtered => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />}
sorter={(a, b) => {
if (a.username < b.username) return -1
else return 1
}}
/>
<Column title="Email" dataIndex="email" key="email"
filterDropdown={({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div style={{ padding: 8 }}>
<Input
placeholder="Search Email"
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => confirm()}
style={{ marginBottom: 8, display: 'block' }}
autoFocus
/>
<Space>
<Button
type="primary"
onClick={() => { confirm() }}
icon={<SearchOutlined />}
>
Search
</Button>
<Button onClick={() => clearFilters()}>
Reset
</Button>
</Space>
</div>
)}
onFilter={(value, record) => record.email.toLowerCase().trim().includes(value.toLowerCase())}
filterIcon={filtered => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />}
/>
<Column title="Permissions" dataIndex="type" key="type" filters={[{ text: "Normal User (0)", value: 0 }, { text: "Challenge Creator (1)", value: 1 }, { text: "Admin (2)", value: 2 }]} onFilter={(value, record) => { return value === record.type }} />
<Column title="Team" dataIndex="team" key="team"
render={(text, row, index) => {
if (text != "N/A") return <Link to={"/Team/" + text}><a style={{ fontWeight: 700 }}>{text}</a></Link>;
else return text;
}}
filterDropdown={({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div style={{ padding: 8 }}>
<Input
placeholder="Search Team"
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => confirm()}
style={{ marginBottom: 8, display: 'block' }}
autoFocus
/>
<Space>
<Button
type="primary"
onClick={() => { confirm() }}
icon={<SearchOutlined />}
>
Search
</Button>
<Button onClick={() => clearFilters()}>
Reset
</Button>
</Space>
</div>
)}
onFilter={(value, record) => record.team.toLowerCase().trim().includes(value.toLowerCase())}
filterIcon={filtered => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />}
/>
<Column title="Category" dataIndex="category" key="category" filters={
this.state.categoryList.map((category) => {
return { text: category, value: category }
})} onFilter={(value, record) => { return value === record.category }} />
<Column title="Verified" dataIndex="verified" key="verified" filters={[{ text: "Verified", value: "True" }, { text: "Unverified", value: "False" }]} onFilter={(value, record) => { return value === record.verified }} />
<Column
title=""
key="action"
render={(text, record) => (
<Dropdown trigger={['click']} overlay={
<Menu>
<Menu.Item onClick={() => {
this.setState({ permissionModal: true, username: record.username, permissionChangeTo: record.type.toString() })
}}>
<span>
Change Permissions <ClusterOutlined />
</span>
</Menu.Item>
<Menu.Item onClick={() => {
this.setState({ passwordResetModal: true, username: record.username })
}}>
<span>
Change Password <KeyOutlined />
</span>
</Menu.Item>
<Menu.Item onClick={() => {
this.setState({ emailChangeModal: true, username: record.username })
}}>
<span>
Change Email <MailOutlined />
</span>
</Menu.Item>
<Menu.Item onClick={() => {
this.setState({ categoryChangeModal: true, username: record.username, participantCategory: record.category })
}}>
<span>
Change Category <ApartmentOutlined />
</span>
</Menu.Item>
</Menu>
} placement="bottomCenter">
<Button>Actions</Button>
</Dropdown>
)}
/>
</Table>
<Divider />
<div className="settings-responsive2" style={{ display: "flex", justifyContent: "space-around" }}>
<Card className="settings-card">
<h3>Disable User Registration: <Switch disabled={this.state.disableLoading} onClick={(value) => this.disableSetting("registerDisable", value)} checked={this.state.registerDisable} /></h3>
<p>Disables user registration for unregistered users. Admins can still create users from this page.</p>
</Card>
<Divider type="vertical" style={{ height: "inherit" }} />
<Card className="settings-card">
<h3>Disable User Logins: <Switch disabled={this.state.disableLoading2} onClick={(value) => this.disableSetting("loginDisable", value)} checked={this.state.loginDisable} /></h3>
<p>Disables user login except for admin users. <br /><b>Note:</b> Users already logged into the platform will remain authenticated as tokens cannot be revoked. If you want to restrict a user from accessing the platform anymore, simply delete their account.</p>
</Card>
<Divider type="vertical" style={{ height: "inherit" }} />
<Card className="settings-card">
<h3>Disable Admin Scores: <Switch disabled={this.state.disableLoading2} onClick={(value) => this.disableSetting("adminShowDisable", value)} checked={this.state.adminShowDisable} /></h3>
<p>Prevents admin scores from showing up on scoreboards and profile pages. Admin solves will still appear under the solve list in challenges. <br /> Please note that disabling/enabling this will require users to reopen ctfx to resync the scoreboard.</p>
</Card>
</div>
<Divider />
<div className="settings-responsive2" style={{ display: "flex", justifyContent: "space-around" }}>
<Card className="settings-card">
<h3>Profile Picture Max Upload Size: <InputNumber
formatter={value => `${value}B`}
parser={value => value.replace('B', '')}
value={this.state.uploadSize}
disabled={this.state.uploadLoading}
onChange={(value) => this.setState({ uploadSize: value })}
onPressEnter={(e) => { this.changeSetting("uploadSize", this.state.uploadSize) }} /></h3>
<p>Sets the maximum file upload size for profile pictures (in Bytes). Press <b>Enter</b> to save</p>
</Card>
<Divider type="vertical" style={{ height: "inherit" }} />
<Card className="settings-card">
<h3>Disable Category Switches: <Switch disabled={this.state.disableLoading2} onClick={(value) => this.disableSetting("categorySwitchDisable", value)} checked={this.state.categorySwitchDisable} /></h3>
<p>Prevents users from switching their scoreboard category. Useful during competitions where you want to lock the user into a category</p>
</Card>
<Divider type="vertical" style={{ height: "inherit" }} />
<Card className="settings-card">
<h3>User Category Management <UserOutlined /></h3>
<Space direction="vertical">
{this.state.categoryList.map((category) => {
return (
<div style={{ display: 'flex', alignItems: "center" }}>
<Input disabled value={category} />
<MinusCircleOutlined onClick={() => { this.removeCategory(category) }} style={{ cursor: "pointer", marginLeft: "1ch", color: "#f5222d" }} />
</div>
)
})}
<div style={{ display: "flex" }}>
<Input value={this.state.newCategoryValue} onChange={(e) => { this.setState({ newCategoryValue: e.target.value }) }} />
<Button
loading={this.state.addCategoryLoading}
style={{ marginLeft: "1ch" }}
type="dashed"
onClick={() => {
this.addCategory()
}}
>
<PlusOutlined /> Add Category
</Button>
</div>
</Space>
</Card>
</div>
<Divider />
<div className="settings-responsive2" style={{ display: "flex", justifyContent: "space-around" }}>
<Card className="settings-card">
<h3>Max Team Size: <InputNumber
value={this.state.teamMaxSize}
onChange={(value) => this.setState({ teamMaxSize: value })}
onPressEnter={(e) => { this.changeSetting("teamMaxSize", this.state.teamMaxSize) }} />
</h3>
<p>Sets the maximum number of members in a team. Press <b>Enter</b> to save</p>
</Card>
<Divider type="vertical" style={{ height: "inherit" }} />
<Card className="settings-card">
<h3>Enable Teams: <Switch disabled={this.state.disableLoading3} onClick={(value) => this.disableSetting("teamMode", value)} checked={this.state.teamMode} /></h3>
<p>Enable teams for the platform. Users in a team will have their scores combined on the scoreboard <br /> Please note that disabling/enabling this will require users to reopen ctfx to resync the scoreboard.</p>
</Card>
<Divider type="vertical" style={{ height: "inherit" }} />
<Card className="settings-card">
<h3>Disable Team Switching: <Switch disabled={this.state.disableLoading3} onClick={(value) => this.disableSetting("teamChangeDisable", value)} checked={this.state.teamChangeDisable} /></h3>
<p>Prevents users from leaving, joining & creating a team. Enable this option if you want to prevent any team changes during a competition</p>
</Card>
</div>
<Divider />
<div className="settings-responsive2" style={{ display: "flex", justifyContent: "space-around" }}>
<Card className="settings-card">
<h3>Enable Password Reset <Switch disabled={this.state.disableLoading2} onClick={(value) => this.disableSetting("forgotPass", value)} checked={this.state.forgotPass} /></h3>
<p>Allow users to use the "Forgot Password" option to reset their password. <br />Please ensure that you have connected to an SMTP server correctly in the "Email" tab</p>
</Card>
<Divider type="vertical" style={{ height: "inherit" }} />
<Card className="settings-card">
<h3>Enable Email Verification <Switch disabled={this.state.disableLoading2} onClick={(value) => this.disableSetting("emailVerify", value)} checked={this.state.emailVerify} /></h3>
<p>Forces newly registered users to <b>verify their email</b> before being able to access the site.</p>
</Card>
<Divider type="vertical" style={{ height: "inherit" }} />
<Card className="settings-card">
<h3>Profile Picture Upload Path
<Input
value={this.state.uploadPath}
onChange={(e) => this.setState({ uploadPath: e.target.value })}
onPressEnter={(e) => { this.changeSetting("uploadPath", this.state.uploadPath) }} /></h3>
<p>Sets the file upload path for profile pictures. Please ensure that the folder has the appropriate permissions <br />set for the Node process to save the file there. Press <b>Enter</b> to save</p>
</Card>
</div>
</Layout>
);
}
Example #9
Source File: userChallengeCreate.js From ctf_platform with MIT License | 4 votes |
CreateChallengeForm = (props) => {
const [form] = Form.useForm();
const [editorValue, setEditorValue] = React.useState("")
const [existingCats, setExistingCats] = React.useState([])
useEffect(() => {
var currentValues = form.getFieldsValue()
currentValues.flags = [""]
form.setFieldsValue(currentValues)
let existCats = []
for (let i = 0; i < props.allCat.length; i++) {
existCats.push(<Option key={props.allCat[i]} value={props.allCat[i]}>{props.allCat[i]}</Option>)
}
setExistingCats(existCats)
}, [])
return (
<Form
form={form}
name="create_challenge_form"
className="create_challenge_form"
onFinish={async (values) => {
props.setState({ edited: false })
//console.log(values)
if (typeof values.flags === "undefined") {
message.warn("Please enter at least 1 flag")
}
else {
//console.log(values)
props.setState({ loading: true })
if (values.visibility === "false") {
values.visibility = false
}
else {
values.visibility = true
}
const category = (typeof values.category1 !== "undefined") ? values.category1 : values.category2
if (typeof values.writeup !== "undefined") {
if (typeof values.writeupComplete === "undefined") {
values.writeupComplete = true
}
}
await fetch(window.ipAddress + "/v1/challenge/new", {
method: 'post',
headers: { 'Content-Type': 'application/json', "Authorization": window.IRSCTFToken },
body: JSON.stringify({
"name": values.name,
"category": category,
"description": values.description,
"points": values.points,
"flags": values.flags,
"tags": values.tags,
"hints": values.hints,
"max_attempts": values.max_attempts,
"visibility": values.visibility,
"writeup": values.writeup,
"writeupComplete": values.writeupComplete
})
}).then((results) => {
return results.json(); //return data in JSON (since its JSON data)
}).then((data) => {
//console.log(data)
if (data.success === true) {
message.success({ content: "Created challenge " + values.name + " successfully!" })
form.resetFields()
}
else {
message.error({ content: "Oops. Unknown error, please contact an admin." })
}
}).catch((error) => {
console.log(error)
message.error({ content: "Oops. Issue connecting with the server or client error, please check console and report the error. " });
})
props.setState({ loading: false })
}
}}
>
<h1>Challenge Name:</h1>
<Form.Item
name="name"
rules={[{ required: true, message: 'Please enter a challenge name' }]}
>
<Input allowClear placeholder="Challenge name" />
</Form.Item>
<Divider />
<h1>Challenge Category:</h1>
<h4>Select an Existing Category: </h4>
<Form.Item
name="category1"
rules={[{ required: !props.state.selectCatDisabled, message: 'Please enter a challenge category' }]}
>
<Select
disabled={props.state.selectCatDisabled}
allowClear
showSearch
placeholder="Select an existing Category"
onChange={(value) => {
if (value) {
props.setState({ inputCatDisabled: true })
}
else {
props.setState({ inputCatDisabled: false })
}
}}
>
{existingCats}
</Select>
</Form.Item>
<h4>Enter a New Category</h4>
<Form.Item
name="category2"
rules={[{ required: !props.state.inputCatDisabled, message: 'Please enter a challenge category' }]}
>
<Input onChange={(e) => {
e.target.value.length > 0 ? props.setState({ selectCatDisabled: true }) : props.setState({ selectCatDisabled: false })
}} disabled={props.state.inputCatDisabled} allowClear placeholder="Enter a new challenge category" />
</Form.Item>
<Divider />
<Suspense fallback={<div style={{ height: "100%", width: "100%", display: "flex", justifyContent: "center", alignItems: "center", zIndex: 15 }}>
<Ellipsis color="#177ddc" size={120} ></Ellipsis>
</div>}>
<h1>Challenge Description (Supports <a href="https://guides.github.com/features/mastering-markdown/" target="_blank" rel="noreferrer">Markdown</a> and <a href="https://github.com/rehypejs/rehype-raw" target="_blank" rel="noreferrer">HTML</a>)):</h1>
<Form.Item
name="description"
rules={[{ required: true, message: 'Please enter a description' }]}
valuePropName={editorValue}
>
<MDEditor value={editorValue} onChange={(value) => { setEditorValue(value) }} preview="edit" />
</Form.Item>
<h3>Challenge Description Preview</h3>
<Card
type="inner"
bordered={true}
bodyStyle={{ backgroundColor: "#262626", textAlign: "center" }}
className="challengeModal"
>
<MarkdownRender>{editorValue}</MarkdownRender>
</Card>
</Suspense>
<Divider />
<div style={{ display: "flex", flexDirection: "row", justifyItems: "space-evenly", marginLeft: "2vw" }}>
<div style={{ display: "flex", flexDirection: "column", justifyContent: "center", width: "35vw" }}>
<h1>Challenge Points:</h1>
<Form.Item
name="points"
rules={[{ required: true, message: 'Please enter challenge points' }, {
type: 'integer',
message: "Please enter a valid integer between 1-100000",
},]}
initialValue={1}
>
<InputNumber min={1} max={100000} style={{ width: "30ch" }} ></InputNumber>
</Form.Item>
<h1>Maximum Number of Attempts (Set to 0 for unlimited)</h1>
<Form.Item
name="max_attempts"
rules={[{ required: true, message: 'Please enter the maximum number of attempts' }, {
type: 'integer',
message: "Please enter a valid integer between 0-10000",
},]}
style={{ alignText: 'center' }}
initialValue={0}
>
<InputNumber min={0} max={10000} style={{ width: "30ch" }}></InputNumber>
</Form.Item>
</div>
<Divider type="vertical" style={{ height: "inherit" }}></Divider>
<div style={{ display: "flex", flexDirection: "column", width: "35vw", marginLeft: "2vw" }}>
<Form.List name="flags" >
{(fields, { add, remove }) => {
return (
<div>
<h1>Flags</h1>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="start">
<Form.Item
{...field}
name={[field.name]}
fieldKey={[field.fieldKey]}
rules={[{ required: true, message: 'Missing flag' }]}
>
<Input style={{ width: "50ch" }} placeholder="Flag" />
</Form.Item>
{fields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
style={{ margin: '0 8px', color: "red" }}
onClick={() => {
remove(field.name);
}}
/>
) : null}
</Space>
))}
<Form.Item>
<Button
type="dashed"
onLoad={() => { if (fields.length < 1) add() }}
onClick={() => {
add();
}}
block
style={{ width: "50ch" }}
>
<PlusOutlined /> Add Flag
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.List name="tags">
{(fields, { add, remove }) => {
return (
<div>
<h1>Tags</h1>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="start">
<Form.Item
{...field}
name={[field.name]}
fieldKey={[field.fieldKey]}
rules={[{ required: true, message: 'Missing tag' }]}
>
<Input placeholder="Tag" style={{ width: "50ch" }} />
</Form.Item>
<MinusCircleOutlined
className="dynamic-delete-button"
style={{ margin: '0 8px', color: "red" }}
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
style={{ width: "50ch" }}
>
<PlusOutlined /> Add Tag
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</div>
</div>
<Divider />
<h1>Hints</h1>
<Form.List name="hints" >
{(fields, { add, remove }) => {
return (
<div>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="start">
<Form.Item
{...field}
name={[field.name, "hint"]}
fieldKey={[field.fieldKey, "hint"]}
rules={[{ required: true, message: 'Missing hint' }]}
>
<Input placeholder="Hint" style={{ width: "20vw" }} />
</Form.Item>
<Form.Item
{...field}
name={[field.name, "cost"]}
fieldKey={[field.fieldKey, "cost"]}
rules={[{ required: true, message: 'Missing cost for hint' }, {
type: 'integer',
message: "Please enter a valid integer between 0-10000",
},]}
>
<InputNumber min={0} max={10000} placeholder="Cost"></InputNumber>
</Form.Item>
<MinusCircleOutlined
style={{ color: "red" }}
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
style={{ width: "50ch" }}
>
<PlusOutlined /> Add Hint
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<h1>Writeup Link (Optional)</h1>
<Form.Item
name="writeup"
rules={[
{
type: 'url',
message: "Please enter a valid link",
}]}
>
<Input allowClear style={{ width: "50ch" }} placeholder="Enter a writeup link for this challenge" />
</Form.Item>
<div style={{ display: "flex", alignContent: "center" }}>
<h4 style={{ marginRight: "2ch" }}>Release Writeup Only After Completion: </h4>
<Form.Item
name="writeupComplete"
>
<Switch defaultChecked />
</Form.Item>
</div>
<h1>Visibility</h1>
<Form.Item
name="visibility"
rules={[{ required: true, message: 'Please set the challenge visibility' }]}
initialValue="false"
>
<Select style={{ width: "10vw" }}>
<Option value="false"><span style={{ color: "#d32029" }}>Hidden <EyeInvisibleOutlined /></span></Option>
<Option value="true"><span style={{ color: "#49aa19" }}>Visible <EyeOutlined /></span></Option>
</Select>
</Form.Item>
<Form.Item>
<div style={{ display: "flex", justifyContent: "space-between", flexDirection: "row" }}>
<div>
<Button style={{ marginBottom: "1.5vh", marginRight: "2vw", backgroundColor: "#d4b106", borderColor: "", color: "white" }} onClick={() => { props.previewChallenge(form.getFieldsValue()); }}>Preview</Button>
<Button loading={props.loadingStatus} type="primary" htmlType="submit" className="login-form-button" style={{ marginBottom: "1.5vh" }}>Create Challenge</Button>
</div>
<div>
<Button style={{ marginRight: "2vw" }} type="primary" danger onClick={() => { form.resetFields() }}>Clear</Button>
</div>
</div>
</Form.Item>
</Form>
);
}
Example #10
Source File: ChannelSetting.js From network-rc with Apache License 2.0 | 4 votes |
export default function ChannelSetting({
saveServerConfig,
serverConfig,
resetChannel,
}) {
const [form] = Form.useForm();
useEffect(() => {
form.resetFields();
}, [serverConfig, form]);
const ui = (index, fields, { add, remove }) => {
return (
<Space align="baseline" split={<Divider type="vertical" />} wrap>
{fields.map((field) => {
const uiId = form.getFieldValue([
"channelList",
index,
"ui",
field.name,
"id",
]);
const uiComponent = serverConfig.uiComponentList.find(
(i) => i.id === uiId
);
return (
<Space key={field.key} align="baseline">
<Form.Item
label="UI"
{...field}
name={[field.name, "id"]}
fieldKey={[field.fieldKey, "id"]}
rules={[{ required: true, message: "客官!选一个!" }]}
>
<Select style={{ width: 80 }}>
{serverConfig.uiComponentList.map(({ id, name }) => (
<Option value={id}> {name} </Option>
))}
</Select>
</Form.Item>
{uiComponent && uiComponent.type === "joystick" && (
<Form.Item
label="轴"
{...field}
name={[field.name, "axis"]}
fieldKey={[field.fieldKey, "axis"]}
rules={[{ required: true, message: "客官!选一个!" }]}
>
<Select style={{ width: 80 }}>
<Option value="x">x</Option>
<Option value="y">y</Option>
</Select>
</Form.Item>
)}
<Form.Item
label="控制方向"
{...field}
name={[field.name, "positive"]}
fieldKey={[field.fieldKey, "positive"]}
valuePropName="checked"
>
<Switch checkedChildren="正向" unCheckedChildren="反向" />
</Form.Item>
<Form.Item
label="控制方式"
{...field}
name={[field.name, "method"]}
fieldKey={[field.fieldKey, "method"]}
>
<Select style={{ width: 80 }}>
<Option value="default">默认</Option>
<Option value="step">步进</Option>
</Select>
</Form.Item>
<Form.Item
label="步进速度"
{...field}
name={[field.name, "speed"]}
fieldKey={[field.fieldKey, "speed"]}
initialValue={0.5}
>
<InputNumber
step={0.1}
min={0}
disabled={
"step" !==
form.getFieldValue([
"channelList",
index,
"ui",
field.name,
"method",
])
}
/>
</Form.Item>
<MinusCircleOutlined onClick={() => remove(field.name)} />
</Space>
);
})}
<PlusCircleOutlined
onClick={() =>
add({
positive: true,
method: "default",
})
}
/>
</Space>
);
};
const keyboard = (index, fields, { add, remove }) => {
const type = form.getFieldValue(["channelList", index, "type"]);
return (
<Space align="baseline" direction="vertical">
{fields.map((field) => {
const method = form.getFieldValue([
"channelList",
index,
"keyboard",
field.name,
"method",
]);
return (
<Space key={field.key} align="baseline">
<Form.Item
{...field}
name={[field.name, "name"]}
fieldKey={[field.fieldKey, "name"]}
rules={[{ required: true, message: "客官!选一个按键!" }]}
extra="键盘按键"
>
<Input style={{ width: 80 }} />
</Form.Item>
{type === "pwm" && (
<Form.Item
{...field}
name={[field.name, "method"]}
fieldKey={[field.fieldKey, "method"]}
defaultValue="default"
extra="控制方式"
>
<Radio.Group size="middle">
<Radio.Button value="default">默认</Radio.Button>
<Radio.Button value="step">步进</Radio.Button>
</Radio.Group>
</Form.Item>
)}
{type === "pwm" && (
<Form.Item
{...field}
name={[field.name, "speed"]}
fieldKey={[field.fieldKey, "speed"]}
extra={
method === "default"
? "-1 ~ 1, 0 为归位"
: "每秒步进速度系数"
}
>
<InputNumber max={1} min={-1} step={0.1} />
</Form.Item>
)}
{/* {type === "switch" && (
<Form.Item
{...field}
name={[field.name, "speed"]}
fieldKey={[field.fieldKey, "speed"]}
extra="高低电平"
>
<Radio.Group size="middle">
<Radio.Button value={1}>高</Radio.Button>
<Radio.Button value={-1}>低</Radio.Button>
</Radio.Group>
</Form.Item>
)} */}
{(method === "default" || type === "switch") && (
<Form.Item
{...field}
name={[field.name, "autoReset"]}
fieldKey={[field.fieldKey, "autoReset"]}
extra="释放键盘是否复位"
valuePropName="checked"
defaultValue={true}
>
<Switch />
</Form.Item>
)}
<MinusCircleOutlined onClick={() => remove(field.name)} />
</Space>
);
})}
<PlusCircleOutlined
onClick={() =>
add({
speed: 1,
autoReset: true,
method: "default",
})
}
/>
</Space>
);
};
const gamepad = (index, fields, { add, remove }) => {
const type = form.getFieldValue(["channelList", index, "type"]);
return (
<Space align="baseline" split={<Divider type="vertical" />} wrap>
{fields.map((field) => {
// const method = form.getFieldValue([
// "channelList",
// index,
// "gamepad",
// field.name,
// "method",
// ]);
return (
<Space key={field.key} align="baseline">
<Form.Item
{...field}
name={[field.name, "name"]}
fieldKey={[field.fieldKey, "name"]}
rules={[{ required: true, message: "客官!选一个!" }]}
extra="摇杆轴或按键"
>
<Select style={{ width: 100 }}>
{gamePadInputList.map(({ value, name }) => (
<Option value={value}> {name} </Option>
))}
</Select>
</Form.Item>
{type === "pwm" && (
<Form.Item
{...field}
name={[field.name, "method"]}
fieldKey={[field.fieldKey, "method"]}
defaultValue="default"
extra="控制方式"
>
<Radio.Group size="middle">
<Radio.Button value="default">默认</Radio.Button>
<Radio.Button value="step">步进</Radio.Button>
</Radio.Group>
</Form.Item>
)}
{type === "pwm" && (
<Form.Item
{...field}
name={[field.name, "speed"]}
fieldKey={[field.fieldKey, "speed"]}
extra="控制系数,-1 ~ 1"
>
<InputNumber min={-1} max={1} step={0.1} />
</Form.Item>
)}
{/* {method === "default" && (
<Form.Item
{...field}
name={[field.name, "autoReset"]}
fieldKey={[field.fieldKey, "autoReset"]}
extra="释放按钮是否复位"
valuePropName="checked"
defaultValue={true}
>
<Switch />
</Form.Item>
)} */}
{/* {type === "switch" && (
<Form.Item
{...field}
name={[field.name, "speed"]}
fieldKey={[field.fieldKey, "speed"]}
extra="高低电平"
>
<Radio.Group size="middle">
<Radio.Button value={1}>高</Radio.Button>
<Radio.Button value={-1}>低</Radio.Button>
</Radio.Group>
</Form.Item>
)} */}
<MinusCircleOutlined onClick={() => remove(field.name)} />
</Space>
);
})}
<PlusCircleOutlined
onClick={() =>
add({
speed: 1,
autoReset: true,
method: "default",
})
}
/>
</Space>
);
};
return (
<Form form={form} onFinish={saveServerConfig} initialValues={serverConfig}>
<Form.List name="channelList">
{(fields, { add, remove }) => (
<Space
key={fields.key}
direction="vertical"
style={{ width: "100%" }}
>
{fields.map((field) => (
<Card
key={field.key}
title={
<Space key={field.key} align="baseline" wrap>
<Form.Item
{...field}
name={[field.name, "enabled"]}
fieldKey={[field.fieldKey, "enabled"]}
valuePropName="checked"
>
<Switch checkedChildren="开启" unCheckedChildren="禁用" />
</Form.Item>
<Form.Item
{...field}
label="名称"
name={[field.name, "name"]}
fieldKey={[field.fieldKey, "name"]}
>
<Input style={{ width: 80 }} />
</Form.Item>
<Button danger onClick={() => remove(field.name)}>
<DeleteOutlined />
</Button>
</Space>
}
>
<Row>
<Space key={field.key} align="baseline" wrap>
<Form.Item
{...field}
label="GPIO"
name={[field.name, "pin"]}
fieldKey={[field.fieldKey, "pin"]}
rules={[{ required: true, message: "仔细想想" }]}
>
<Select style={{ width: 60 }}>
{pins.map((pin) => (
<Option key={pin} value={pin}>
{pin}
</Option>
))}
</Select>
</Form.Item>
<Form.Item
{...field}
label="类型"
name={[field.name, "type"]}
fieldKey={[field.fieldKey, "type"]}
rules={[{ required: true, message: "你不能没有我!" }]}
>
<Select style={{ width: 80 }}>
{types.map(({ value, label }) => (
<Option key={value} value={value}>
{label}
</Option>
))}
</Select>
</Form.Item>
{form.getFieldValue([
"channelList",
field.fieldKey,
"type",
]) === "pwm" && (
<>
<Form.Item
{...field}
label="最大值"
name={[field.name, "valuePostive"]}
fieldKey={[field.fieldKey, "valuePostive"]}
>
<InputNumber
step={0.1}
max={2}
min={form.getFieldValue([
"channelList",
field.fieldKey,
"valueReset",
])}
/>
</Form.Item>
<Form.Item
{...field}
label="复位值"
name={[field.name, "valueReset"]}
fieldKey={[field.fieldKey, "valueReset"]}
shouldUpdate
>
<InputNumber
step={0.1}
min={form.getFieldValue([
"channelList",
field.fieldKey,
"valueNegative",
])}
max={form.getFieldValue([
"channelList",
field.fieldKey,
"valuePostive",
])}
/>
</Form.Item>
<Form.Item
{...field}
label="最小值"
name={[field.name, "valueNegative"]}
fieldKey={[field.fieldKey, "valueNegative"]}
>
<InputNumber
step={0.1}
min={-2}
max={form.getFieldValue([
"channelList",
field.fieldKey,
"valueReset",
])}
/>
</Form.Item>
</>
)}
</Space>
</Row>
<Collapse align="start">
<Panel header="UI 控件" size="small">
<Form.List name={[field.name, "ui"]}>
{(...props) => ui(field.fieldKey, ...props)}
</Form.List>
</Panel>
<Panel header="键盘" size="small">
<Form.List name={[field.name, "keyboard"]}>
{(...props) => keyboard(field.fieldKey, ...props)}
</Form.List>
</Panel>
<Panel header="手柄" size="small">
<Form.List name={[field.name, "gamepad"]}>
{(...props) => gamepad(field.fieldKey, ...props)}
</Form.List>
</Panel>
<Panel header="手机重力感应" size="small">
<Form.Item
{...field}
label="方向"
name={[field.name, "orientation", "axis"]}
fieldKey={[field.fieldKey, "orientation", "axis"]}
>
<Select style={{ width: 80 }} allowClear>
{[
{ name: "水平", value: "x" },
{ name: "垂直", value: "y" },
].map((i) => (
<Option key={i.value} value={i.value}>
{i.name}
</Option>
))}
</Select>
</Form.Item>
<Form.Item
{...field}
label="系数"
name={[field.name, "orientation", "coefficient"]}
fieldKey={[field.fieldKey, "orientation", "coefficient"]}
>
<Slider defaultValue={1} max={2} min={-2} step={0.01} />
</Form.Item>
</Panel>
</Collapse>
</Card>
))}
<Form.Item>
<Button
onClick={() =>
add({
id: uuid(),
enabled: true,
valuePostive: 1,
valueNegative: -1,
valueReset: 0,
})
}
block
icon={<PlusOutlined />}
>
添加通道
</Button>
</Form.Item>
</Space>
)}
</Form.List>
<Form.Item>
<Space>
<Button type="primary" htmlType="submit">
保存
</Button>
<Button
type="danger"
onClick={() => {
resetChannel();
store.remove("ui-position");
}}
>
恢复默认通道设置
</Button>
</Space>
</Form.Item>
</Form>
);
}
Example #11
Source File: SoundSetting.js From network-rc with Apache License 2.0 | 4 votes |
export default function SoundSetting({
micVolume,
audioList,
saveServerConfig,
host,
}) {
const [form] = Form.useForm();
const { data: currentSpeaker = {} } = useRequest(
`${protocol}//${host}/api/speaker/current`,
{
onSuccess: () => {
form.resetFields();
},
}
);
const { data: speakerList = [] } = useRequest(
`${protocol}//${host}/api/speaker`
);
const { run: setSpeaker } = useRequest(
() => ({
url: `${protocol}//${host}/api/speaker/set`,
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ name: form.getFieldValue("currentSpeakerName") }),
}),
{
manual: true,
}
);
const { run: setSpeakerVolume } = useRequest(
() => ({
url: `${protocol}//${host}/api/speaker/volume`,
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: form.getFieldValue("currentSpeakerName"),
volume: form.getFieldValue("currentSpeakerVolume"),
}),
}),
{
manual: true,
}
);
const { data: currentMic = {} } = useRequest(
`${protocol}//${host}/api/mic/current`,
{
onSuccess: () => {
form.resetFields();
},
}
);
const { data: micList = [] } = useRequest(`${protocol}//${host}/api/mic`);
const { run: setMic } = useRequest(
() => ({
url: `${protocol}//${host}/api/mic/set`,
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ name: form.getFieldValue("currentMicName") }),
}),
{
manual: true,
}
);
const { run: setMicVolume } = useRequest(
() => ({
url: `${protocol}//${host}/api/mic/volume`,
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: form.getFieldValue("currentMicName"),
volume: form.getFieldValue("currentMicVolume"),
}),
}),
{
manual: true,
}
);
return (
<Form
form={form}
{...layout}
initialValues={{
audioList,
currentSpeakerName: currentSpeaker?.name,
currentSpeakerVolume: currentSpeaker?.volume || 0,
currentMicName: currentMic?.name,
currentMicVolume: currentMic?.volume || 0,
}}
>
<Form.Item label="喇叭音量" name="currentSpeakerVolume">
<Slider
min={0}
max={100}
value={currentSpeaker?.volume}
onAfterChange={setSpeakerVolume}
/>
</Form.Item>
<Form.Item label="输出设备" name="currentSpeakerName">
<Select onChange={setSpeaker}>
{speakerList.map(({ name, displayName, volume }) => (
<Option key={name} value={name}>
{`${displayName}(${volume}%)`}
</Option>
))}
</Select>
</Form.Item>
<Form.Item label="麦克风音量" name="currentMicVolume">
<Slider
min={0}
max={100}
value={micVolume}
onAfterChange={setMicVolume}
/>
</Form.Item>
<Form.Item label="选择麦克风" name="currentMicName">
<Select onChange={setMic}>
{micList.map(({ name, displayName, volume }) => (
<Option key={name} value={name}>
{`${displayName}(${volume}%)`}
</Option>
))}
</Select>
</Form.Item>
<Form.Item label="快捷播放">
<Form.List name="audioList">
{(fields, { add, remove }) => (
<>
{fields.map(({ key, name, ...restField }) => (
<Space
key={key}
style={{ display: "flex", marginBottom: 8 }}
align="baseline"
>
<Form.Item {...restField} name={[name, "type"]} extra="类型">
<Select onChange={setSpeaker}>
<Option key="audio" value="audio">
文件
</Option>
<Option key="test" value="text">
语音
</Option>
<Option key="stop" value="stop">
停止
</Option>
</Select>
</Form.Item>
{form.getFieldValue(["audioList", name, "type"]) ===
"audio" && (
<>
<Form.Item
{...restField}
name={[name, "name"]}
rules={[
{ required: true, message: "写个名字日后好相见" },
]}
style={{ width: 80 }}
extra="名称"
>
<Input placeholder="写个名字日后好相见" />
</Form.Item>
<Form.Item
{...restField}
name={[name, "path"]}
extra="文件在树莓派上完整路径"
>
<Input placeholder="文件路径" />
{/* <Upload /> */}
</Form.Item>
</>
)}
{form.getFieldValue(["audioList", name, "type"]) ===
"text" && (
<>
<Form.Item
{...restField}
name={[name, "text"]}
extra="语音播报文本"
>
<Input placeholder="语音播报文本" allowClear />
</Form.Item>
</>
)}
<Form.Item
{...restField}
name={[name, "keyboard"]}
style={{ width: 80 }}
extra="⌨️ 按键"
>
<Input placeholder="键盘" prefix="⌨️" />
</Form.Item>
<Form.Item
{...restField}
name={[name, "gamepadButton"]}
extra={
<span>
? 编号
<a
href="https://gamepad-tester.com"
target="_blank"
rel="noreferrer"
>
测试网页
</a>
</span>
}
>
<InputNumber min={0} max={99} />
</Form.Item>
<Form.Item
{...restField}
name={[name, "showFooter"]}
extra="在底部显示"
valuePropName="checked"
>
<Switch />
</Form.Item>
<Button
icon={<MinusCircleOutlined />}
type="dashed"
onClick={() => remove(name)}
></Button>
</Space>
))}
<Form.Item>
<Space>
<Button
icon={<PlusCircleOutlined />}
type="dashed"
onClick={() => add({ showFooter: false })}
></Button>
<Button
type="primary"
onClick={() => {
saveServerConfig({
audioList: form.getFieldValue("audioList"),
});
}}
>
保存
</Button>
</Space>
</Form.Item>
</>
)}
</Form.List>
</Form.Item>
</Form>
);
}
Example #12
Source File: UISetting.js From network-rc with Apache License 2.0 | 4 votes |
export default function UISetting({ saveServerConfig, serverConfig }) {
const [form] = Form.useForm();
return (
<Form form={form} onFinish={saveServerConfig} initialValues={serverConfig}>
<Form.List name="uiComponentList">
{(fields, { add, remove }) => (
<>
{fields.map((field) => (
<Row>
<Space key={field.key} align="baseline">
<Form.Item
{...field}
name={[field.name, "enabled"]}
fieldKey={[field.fieldKey, "enabled"]}
valuePropName="checked"
>
<Switch checkedChildren="显示" unCheckedChildren="隐藏" />
</Form.Item>
<Form.Item
{...field}
label="名称"
name={[field.name, "name"]}
fieldKey={[field.fieldKey, "name"]}
>
<Input style={{ width: 80 }} />
</Form.Item>
<Form.Item
{...field}
label="类型"
name={[field.name, "type"]}
fieldKey={[field.fieldKey, "type"]}
rules={[{ required: true, message: "你还没选" }]}
>
<Select style={{ width: 72 }}>
{types.map(({ value, label }) => (
<Option key={value} value={value}>
{label}
</Option>
))}
</Select>
</Form.Item>
<Form.Item
{...field}
label="释放归位"
name={[field.name, "autoReset"]}
fieldKey={[field.fieldKey, "autoReset"]}
valuePropName="checked"
extra="释放 UI 控件是否自动归位"
>
<Switch />
</Form.Item>
{form.getFieldValue([
"uiComponentList",
field.fieldKey,
"type",
]) === "slider" && (
<Form.Item
{...field}
label="方向"
name={[field.name, "vertical"]}
fieldKey={[field.fieldKey, "vertical"]}
shouldUpdate
rules={[{ required: true, message: "你还没选" }]}
>
<Select style={{ width: 80 }}>
<Option value={false}> 横向 </Option>
<Option value={true}> 垂直 </Option>
</Select>
</Form.Item>
)}
<MinusCircleOutlined onClick={() => remove(field.name)} />
</Space>
</Row>
))}
<Form.Item>
<Button
type="dashed"
onClick={() =>
add({
id: uuid(),
enabled: true,
})
}
block
icon={<PlusOutlined />}
>
添加控制组件
</Button>
</Form.Item>
</>
)}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
保存
</Button>
</Form.Item>
</Form>
);
}
Example #13
Source File: dynamic-form-item.jsx From virtuoso-design-system with MIT License | 4 votes |
DynamicFieldSet = () => {
const onFinish = values => {
console.log('Received values of form:', values);
};
return (
<Form name="dynamic_form_item" {...formItemLayoutWithOutLabel} onFinish={onFinish}>
<Form.List
name="names"
rules={[
{
validator: async (_, names) => {
if (!names || names.length < 2) {
return Promise.reject(new Error('At least 2 passengers'));
}
},
},
]}
>
{(fields, { add, remove }, { errors }) => (
<>
{fields.map((field, index) => (
<Form.Item
{...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
label={index === 0 ? 'Passengers' : ''}
required={false}
key={field.key}
>
<Form.Item
{...field}
validateTrigger={['onChange', 'onBlur']}
rules={[
{
required: true,
whitespace: true,
message: "Please input passenger's name or delete this field.",
},
]}
noStyle
>
<Input placeholder="passenger name" style={{ width: '60%' }} />
</Form.Item>
{fields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
onClick={() => remove(field.name)}
/>
) : null}
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => add()}
style={{ width: '60%' }}
icon={<PlusOutlined />}
>
Add field
</Button>
<Button
type="dashed"
onClick={() => {
add('The head item', 0);
}}
style={{ width: '60%', marginTop: '20px' }}
icon={<PlusOutlined />}
>
Add field at head
</Button>
<Form.ErrorList errors={errors} />
</Form.Item>
</>
)}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
}