@ant-design/icons#EyeOutlined JavaScript Examples
The following examples show how to use
@ant-design/icons#EyeOutlined.
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: FocusButton.jsx From ui with MIT License | 6 votes |
FocusButton = (props) => {
const { store, lookupKey, experimentId } = props;
const dispatch = useDispatch();
const focusData = useSelector((state) => state.cellInfo.focus);
const buttonRef = useRef(null);
const onClick = (e) => {
// Prevent clicking button from clicking the component it is embedded in (i.e. table row).
e.stopPropagation();
// Lose focus so the button changes color from blue to black when you click on it.
buttonRef.current.blur();
dispatch(setCellInfoFocus(experimentId, store, lookupKey));
};
const focused = focusData.store === store && focusData.key === lookupKey;
return (
<Tooltip placement='right' title={`${(focused) ? 'Hide color on' : 'Show color on'} embedding`} destroyTooltipOnHide>
<Button
type='dashed'
aria-label='Show on embedding'
style={{ background: 'none' }}
size='small'
onClick={onClick}
ref={buttonRef}
>
{focused
? (<EyeTwoTone style={{ cursor: 'pointer' }} />)
: (<EyeOutlined style={{ cursor: 'pointer' }} />)}
</Button>
</Tooltip>
);
}
Example #2
Source File: CrudDataTable.jsx From starter-antd-admin-crud-auth-mern with MIT License | 6 votes |
function DropDownRowMenu({ row }) {
const dispatch = useDispatch();
const { crudContextAction } = useCrudContext();
const { panel, collapsedBox, modal, readBox, editBox } = crudContextAction;
const item = useSelector(selectItemById(row._id));
const Show = () => {
dispatch(crud.currentItem(item));
panel.open();
collapsedBox.open();
readBox.open();
};
function Edit() {
dispatch(crud.currentAction("update", item));
editBox.open();
panel.open();
collapsedBox.open();
}
function Delete() {
dispatch(crud.currentAction("delete", item));
modal.open();
}
return (
<Menu style={{ width: 130 }}>
<Menu.Item key={`${uniqueId()}`} icon={<EyeOutlined />} onClick={Show}>
Show
</Menu.Item>
<Menu.Item key={`${uniqueId()}`} icon={<EditOutlined />} onClick={Edit}>
Edit
</Menu.Item>
<Menu.Item
key={`${uniqueId()}`}
icon={<DeleteOutlined />}
onClick={Delete}
>
Delete
</Menu.Item>
</Menu>
);
}
Example #3
Source File: CrudDataTable.jsx From starter-antd-admin-crud-auth-mern with MIT License | 6 votes |
function DropDownRowMenu({ row }) {
const dispatch = useDispatch();
const { crudContextAction } = useCrudContext();
const { panel, collapsedBox, modal, readBox, editBox } = crudContextAction;
const item = useSelector(selectItemById(row._id));
const Show = () => {
dispatch(crud.currentItem(item));
panel.open();
collapsedBox.open();
readBox.open();
};
function Edit() {
dispatch(crud.currentAction("update", item));
editBox.open();
panel.open();
collapsedBox.open();
}
function Delete() {
dispatch(crud.currentAction("delete", item));
modal.open();
}
return (
<Menu style={{ width: 130 }}>
<Menu.Item key={`${uniqueId()}`} icon={<EyeOutlined />} onClick={Show}>
Show
</Menu.Item>
<Menu.Item key={`${uniqueId()}`} icon={<EditOutlined />} onClick={Edit}>
Edit
</Menu.Item>
<Menu.Item
key={`${uniqueId()}`}
icon={<DeleteOutlined />}
onClick={Delete}
>
Delete
</Menu.Item>
</Menu>
);
}
Example #4
Source File: index.jsx From starter-antd-admin-crud-auth-mern with MIT License | 6 votes |
function DropDownRowMenu({ row }) {
const Show = () => {};
function Edit() {}
function Delete() {}
return (
<Menu style={{ width: 130 }}>
<Menu.Item icon={<EyeOutlined />} onClick={Show}>
Show
</Menu.Item>
<Menu.Item icon={<EditOutlined />} onClick={Edit}>
Edit
</Menu.Item>
<Menu.Item icon={<DeleteOutlined />} onClick={Delete}>
Delete
</Menu.Item>
</Menu>
);
}
Example #5
Source File: DataTableDropMenu.jsx From erp-crm with MIT License | 6 votes |
export default function DataTableDropMenu({ row, entity }) {
const dispatch = useDispatch();
const { erpContextAction } = useErpContext();
const { readPanel, updatePanel, modal } = erpContextAction;
const item = useSelector(selectItemById(row._id));
function Read() {
dispatch(erp.currentItem({ data: item }));
readPanel.open();
}
function Edit() {
dispatch(erp.currentAction({ actionType: 'update', data: item }));
updatePanel.open();
}
function Delete() {
dispatch(erp.currentAction({ actionType: 'delete', data: item }));
modal.open();
}
function Download() {
window.open(`${DOWNLOAD_BASE_URL}${entity}/${entity}-${row._id}.pdf`, '_blank');
}
return (
<Menu style={{ minWidth: 130 }}>
<Menu.Item key={`${uniqueId()}`} icon={<EyeOutlined />} onClick={Read}>
Show
</Menu.Item>
<Menu.Item key={`${uniqueId()}`} icon={<EditOutlined />} onClick={Edit}>
Edit
</Menu.Item>
<Menu.Item key={`${uniqueId()}`} icon={<FilePdfOutlined />} onClick={Download}>
Download
</Menu.Item>
<Menu.Item key={`${uniqueId()}`} icon={<DeleteOutlined />} onClick={Delete}>
Delete
</Menu.Item>
</Menu>
);
}
Example #6
Source File: CrudDataTable.jsx From erp-crm with MIT License | 6 votes |
function DropDownRowMenu({ row }) {
const dispatch = useDispatch();
const { crudContextAction } = useCrudContext();
const { panel, collapsedBox, modal, readBox, editBox } = crudContextAction;
const item = useSelector(selectItemById(row._id));
const Show = () => {
dispatch(crud.currentItem({ data: item }));
panel.open();
collapsedBox.open();
readBox.open();
};
function Edit() {
dispatch(crud.currentItem({ data: item }));
dispatch(crud.currentAction({ actionType: 'update', data: item }));
editBox.open();
panel.open();
collapsedBox.open();
}
function Delete() {
dispatch(crud.currentAction({ actionType: 'delete', data: item }));
modal.open();
}
return (
<Menu style={{ width: 130 }}>
<Menu.Item key={`${uniqueId()}`} icon={<EyeOutlined />} onClick={Show}>
Show
</Menu.Item>
<Menu.Item key={`${uniqueId()}`} icon={<EditOutlined />} onClick={Edit}>
Edit
</Menu.Item>
<Menu.Item key={`${uniqueId()}`} icon={<DeleteOutlined />} onClick={Delete}>
Delete
</Menu.Item>
</Menu>
);
}
Example #7
Source File: index.jsx From erp-crm with MIT License | 6 votes |
function DropDownRowMenu({ row }) {
const Show = () => {};
function Edit() {}
function Delete() {}
return (
<Menu style={{ width: 130 }}>
<Menu.Item icon={<EyeOutlined />} onClick={Show}>
Show
</Menu.Item>
<Menu.Item icon={<EditOutlined />} onClick={Edit}>
Edit
</Menu.Item>
<Menu.Item icon={<DeleteOutlined />} onClick={Delete}>
Delete
</Menu.Item>
</Menu>
);
}
Example #8
Source File: Relations.js From label-studio-frontend with Apache License 2.0 | 6 votes |
RelationsComponent = ({ store }) => {
const annotation = store.annotationStore.selected;
const { relations } = annotation.relationStore;
const hasRelations = relations.length > 0;
const relationsUIVisible = annotation.relationStore.showConnections;
return (
<Block name="relations">
{/* override LS styles' height */}
<Elem name="header">
<Elem name="title">Relations ({relations.length})</Elem>
{hasRelations && (
<Button
size="small"
type="link"
icon={relationsUIVisible ? <EyeInvisibleOutlined /> : <EyeOutlined />}
onClick={() => annotation.relationStore.toggleConnections()}
className={[relationsUIVisible ? styles.uihidden : styles.uivisible, globalStyles.link]}
/>
)}
</Elem>
<Elem name="content">
{hasRelations ? (
<List
size="small"
bordered
itemLayout="vertical"
className={styles.list}
dataSource={annotation.relationStore.relations}
renderItem={item => <ListItem item={item} />}
/>
) : (
<p>No Relations added yet</p>
)}
</Elem>
</Block>
);
}
Example #9
Source File: _config.jsx From documentation with MIT License | 6 votes |
features = [
{
title: translate({ id: "pages.home.features.logic.title" }),
Icon: EyeOutlined,
description: translate({ id: "pages.home.features.logic.description" }),
},
{
title: translate({ id: "pages.home.features.adaptability.title" }),
Icon: LoginOutlined,
description: translate({ id: "pages.home.features.adaptability.description" }),
},
{
title: translate({ id: "pages.home.features.debt.title" }),
Icon: FileProtectOutlined,
description: translate({ id: "pages.home.features.debt.description" }),
},
{
title: translate({ id: "pages.home.features.shared.title" }),
Icon: BuildOutlined,
description: translate({ id: "pages.home.features.shared.description" }),
},
]
Example #10
Source File: Predictions.js From label-studio-frontend with Apache License 2.0 | 5 votes |
Prediction = observer(({ item, store }) => {
const toggleVisibility = e => {
e.preventDefault();
e.stopPropagation();
item.toggleVisibility();
const c = document.getElementById(`c-${item.id}`);
if (c) c.style.display = item.hidden ? "none" : "unset";
};
const highlight = () => {
const c = document.getElementById(`c-${item.id}`);
if (c) c.classList.add("hover");
};
const unhighlight = () => {
const c = document.getElementById(`c-${item.id}`);
if (c) c.classList.remove("hover");
};
return (
<List.Item
key={item.id}
className={item.selected ? `${styles.annotation} ${styles.annotation_selected}` : styles.annotation}
onClick={() => {
!item.selected && store.annotationStore.selectPrediction(item.id);
}}
onMouseEnter={highlight}
onMouseLeave={unhighlight}
>
<div className={styles.itembtns}>
<div>
<div className={styles.title}>{item.createdBy ? `Model (${item.createdBy})` : null}</div>
Created
<i>{item.createdAgo ? ` ${item.createdAgo} ago` : ` ${Utils.UDate.prettyDate(item.createdDate)}`}</i>
</div>
<div className={styles.buttons}>
{item.selected && (
<Tooltip placement="topLeft" title="Add a new annotation based on this prediction">
<Button
size="small"
onClick={ev => {
ev.preventDefault();
const cs = store.annotationStore;
const p = cs.selected;
const c = cs.addAnnotationFromPrediction(p);
// this is here because otherwise React doesn't re-render the change in the tree
window.setTimeout(function() {
store.annotationStore.selectAnnotation(c.id);
}, 50);
}}
>
<CopyOutlined />
</Button>
</Tooltip>
)}
{store.annotationStore.viewingAllAnnotations && (
<Button size="small" type="primary" ghost onClick={toggleVisibility}>
{item.hidden ? <EyeInvisibleOutlined /> : <EyeOutlined />}
</Button>
)}
</div>
</div>
</List.Item>
);
})
Example #11
Source File: AdminDataTable.jsx From erp-crm with MIT License | 5 votes |
function DropDownRowMenu({ row }) {
const dispatch = useDispatch();
const { crudContextAction } = useCrudContext();
const { panel, collapsedBox, modal, advancedBox, readBox, editBox } = crudContextAction;
const item = useSelector(selectItemById(row._id));
const Show = () => {
dispatch(crud.currentItem({ data: item }));
panel.open();
collapsedBox.open();
readBox.open();
};
function Edit() {
dispatch(crud.currentItem({ data: item }));
dispatch(crud.currentAction({ actionType: 'update', data: item }));
editBox.open();
panel.open();
collapsedBox.open();
}
function UpdatePassword() {
dispatch(crud.currentItem({ data: item }));
dispatch(crud.currentAction({ actionType: 'update', data: item }));
advancedBox.open();
panel.open();
collapsedBox.open();
}
function Delete() {
dispatch(crud.currentAction({ actionType: 'delete', data: item }));
modal.open();
}
return (
<Menu style={{ minWidth: 130 }}>
<Menu.Item key={`${uniqueId()}`} icon={<EyeOutlined />} onClick={Show}>
Show
</Menu.Item>
<Menu.Item key={`${uniqueId()}`} icon={<EditOutlined />} onClick={Edit}>
Edit
</Menu.Item>
<Menu.Item key={`${uniqueId()}`} icon={<LockOutlined />} onClick={UpdatePassword}>
Update Password
</Menu.Item>
<Menu.Item key={`${uniqueId()}`} icon={<DeleteOutlined />} onClick={Delete}>
Delete
</Menu.Item>
</Menu>
);
}
Example #12
Source File: DataTableDropMenu.jsx From erp-crm with MIT License | 5 votes |
export default function DataTableDropMenu({ row, entity }) {
const dispatch = useDispatch();
const { erpContextAction } = useErpContext();
const { readPanel, updatePanel, recordPanel, modal } = erpContextAction;
const item = useSelector(selectItemById(row._id));
function Read() {
dispatch(erp.currentItem({ data: item }));
readPanel.open();
}
function RecordPayment() {
dispatch(erp.currentAction({ actionType: 'recordPayment', data: item }));
recordPanel.open();
dispatch(erp.currentItem({ data: item }));
}
function Edit() {
dispatch(erp.currentAction({ actionType: 'update', data: item }));
updatePanel.open();
}
function Delete() {
dispatch(erp.currentAction({ actionType: 'delete', data: item }));
modal.open();
}
function Download() {
window.open(`${DOWNLOAD_BASE_URL}${entity}/${entity}-${row._id}.pdf`, '_blank');
}
return (
<Menu style={{ minWidth: 130 }}>
<Menu.Item key={`${uniqueId()}`} icon={<EyeOutlined />} onClick={Read}>
Show
</Menu.Item>
<Menu.Item key={`${uniqueId()}`} icon={<CreditCardOutlined />} onClick={RecordPayment}>
Record Payment
</Menu.Item>
<Menu.Item key={`${uniqueId()}`} icon={<EditOutlined />} onClick={Edit}>
Edit
</Menu.Item>
<Menu.Item key={`${uniqueId()}`} icon={<FilePdfOutlined />} onClick={Download}>
Download
</Menu.Item>
<Menu.Item key={`${uniqueId()}`} icon={<DeleteOutlined />} onClick={Delete}>
Delete
</Menu.Item>
</Menu>
);
}
Example #13
Source File: AdminDataTable.jsx From starter-antd-admin-crud-auth-mern with MIT License | 5 votes |
function DropDownRowMenu({ row }) {
const dispatch = useDispatch();
const { crudContextAction } = useCrudContext();
const {
panel,
collapsedBox,
modal,
advancedBox,
readBox,
editBox,
} = crudContextAction;
const item = useSelector(selectItemById(row._id));
const Show = () => {
dispatch(crud.currentItem(item));
panel.open();
collapsedBox.open();
readBox.open();
};
function Edit() {
dispatch(crud.currentAction("update", item));
editBox.open();
panel.open();
collapsedBox.open();
}
function UpdatePassword() {
dispatch(crud.currentAction("update", item));
advancedBox.open();
panel.open();
collapsedBox.open();
}
function Delete() {
dispatch(crud.currentAction("delete", item));
modal.open();
}
return (
<Menu style={{ minWidth: 130 }}>
<Menu.Item key={`${uniqueId()}`} icon={<EyeOutlined />} onClick={Show}>
Show
</Menu.Item>
<Menu.Item key={`${uniqueId()}`} icon={<EditOutlined />} onClick={Edit}>
Edit
</Menu.Item>
<Menu.Item
key={`${uniqueId()}`}
icon={<LockOutlined />}
onClick={UpdatePassword}
>
Update Password
</Menu.Item>
<Menu.Item
key={`${uniqueId()}`}
icon={<DeleteOutlined />}
onClick={Delete}
>
Delete
</Menu.Item>
</Menu>
);
}
Example #14
Source File: Annotations.js From label-studio-frontend with Apache License 2.0 | 4 votes |
Annotation = observer(({ item, store }) => {
const removeHoney = () => (
<Tooltip placement="topLeft" title="Unset this result as a ground truth">
<Button
size="small"
type="primary"
onClick={ev => {
ev.preventDefault();
item.setGroundTruth(false);
}}
>
<StarOutlined />
</Button>
</Tooltip>
);
const setHoney = () => {
const title = item.ground_truth
? "Unset this result as a ground truth"
: "Set this result as a ground truth";
return (
<Tooltip placement="topLeft" title={title}>
<Button
size="small"
look="link"
onClick={ev => {
ev.preventDefault();
item.setGroundTruth(!item.ground_truth);
}}
>
{item.ground_truth ? (
<StarFilled />
) : (
<StarOutlined />
)}
</Button>
</Tooltip>
);
};
const toggleVisibility = e => {
e.preventDefault();
e.stopPropagation();
item.toggleVisibility();
const c = document.getElementById(`c-${item.id}`);
if (c) c.style.display = item.hidden ? "none" : "unset";
};
const highlight = () => {
const c = document.getElementById(`c-${item.id}`);
if (c) c.classList.add("hover");
};
const unhighlight = () => {
const c = document.getElementById(`c-${item.id}`);
if (c) c.classList.remove("hover");
};
/**
* Default badge for saved annotations
*/
let badge = <Badge status="default" />;
/**
*
*/
let annotationID;
/**
* Title of card
*/
if (item.userGenerate && !item.sentUserGenerate) {
annotationID = <span className={styles.title}>Unsaved Annotation</span>;
} else {
if (item.pk) {
annotationID = <span className={styles.title}>ID {item.pk}</span>;
} else if (item.id) {
annotationID = <span className={styles.title}>ID {item.id}</span>;
}
}
/**
* Badge for processing of user generate annotation
*/
if (item.userGenerate) {
badge = <Badge status="processing" />;
}
/**
* Badge for complete of user generate annotation
*/
if (item.userGenerate && item.sentUserGenerate) {
badge = <Badge status="success" />;
}
const btnsView = () => {
const confirm = () => {
// ev.preventDefault();
// debugger;
item.list.deleteAnnotation(item);
};
return (
<div className={styles.buttons}>
{store.hasInterface("ground-truth") && (item.ground_truth ? removeHoney() : setHoney())}
{store.hasInterface("annotations:delete") && (
<Tooltip placement="topLeft" title="Delete selected annotation">
<Popconfirm
placement="bottomLeft"
title={"Please confirm you want to delete this annotation"}
onConfirm={confirm}
okText="Delete"
okType="danger"
cancelText="Cancel"
>
<Button size="small" danger style={{ background: "transparent" }}>
<DeleteOutlined />
</Button>
</Popconfirm>
</Tooltip>
)}
</div>
);
};
return (
<List.Item
key={item.id}
className={item.selected ? `${styles.annotation} ${styles.annotation_selected}` : styles.annotation}
onClick={() => {
!item.selected && store.annotationStore.selectAnnotation(item.id);
}}
onMouseEnter={highlight}
onMouseLeave={unhighlight}
>
<div className={styles.annotationcard}>
<div>
<div className={styles.title}>
{badge}
{annotationID}
</div>
{item.pk ? "Created" : "Started"}
<i>{item.createdAgo ? ` ${item.createdAgo} ago` : ` ${Utils.UDate.prettyDate(item.createdDate)}`}</i>
{item.createdBy && item.pk ? ` by ${item.createdBy}` : null}
<DraftPanel item={item} />
</div>
{/* platform uses was_cancelled so check both */}
{store.hasInterface("skip") && (item.skipped || item.was_cancelled) && (
<Tooltip placement="topLeft" title="Skipped annotation">
<StopOutlined className={styles.skipped} />
</Tooltip>
)}
{store.annotationStore.viewingAllAnnotations && (
<Button size="small" type="primary" ghost onClick={toggleVisibility}>
{item.hidden ? <EyeInvisibleOutlined /> : <EyeOutlined />}
</Button>
)}
{item.selected && btnsView()}
</div>
</List.Item>
);
})
Example #15
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 #16
Source File: HeatmapMetadataTrackSettings.jsx From ui with MIT License | 4 votes |
HeatmapMetadataTrackSettings = (props) => {
const dispatch = useDispatch();
const { componentType } = props;
const hierarchy = useSelector(getCellSetsHierarchy());
const selectedTracks = useSelector(
(state) => state.componentConfig[componentType]?.config?.selectedTracks,
_.isEqual,
);
const [listData, setListData] = useState([]);
useEffect(() => {
if (hierarchy === null || _.isEmpty(hierarchy)) return;
const selectedTracksListData = convertToReorderableListData(
selectedTracks, true, hierarchy,
);
const unselectedTracks = _.difference(hierarchy.map(({ key }) => key), selectedTracks);
const unselectedTracksListData = convertToReorderableListData(
unselectedTracks, false, hierarchy,
);
setListData([...selectedTracksListData, ...unselectedTracksListData]);
}, [selectedTracks]);
const setTrackSelected = (selected, key) => {
const newSelectedTracks = [...selectedTracks];
if (selected) {
newSelectedTracks.push(key);
} else {
_.pull(newSelectedTracks, key);
}
dispatch(
updatePlotConfig(componentType, {
selectedTracks: newSelectedTracks,
}),
);
};
const setTrackOrder = (reorderedTracks) => {
const reorderedSelectedTrackKeys = reorderedTracks
.filter(({ disabledReorder }) => !disabledReorder)
.map(({ key }) => key);
dispatch(
updatePlotConfig(componentType, {
selectedTracks: reorderedSelectedTrackKeys,
}),
);
};
const leftItem = (trackDataItem) => (
<Switch
checkedChildren={<EyeOutlined />}
unCheckedChildren={<EyeInvisibleOutlined />}
value={trackDataItem.key}
checked={!trackDataItem.disabledReorder}
onChange={(selected) => {
setTrackSelected(selected, trackDataItem.key);
}}
/>
);
const rightItem = (trackDataItem) => (
hierarchy.filter((current) => current.key === trackDataItem.key)[0].name
);
return (
<div style={{ padding: '5px' }}>
<ReorderableList
onChange={setTrackOrder}
listData={listData}
leftItem={leftItem}
rightItem={rightItem}
/>
</div>
);
}
Example #17
Source File: OfflineSession.js From next-terminal with GNU Affero General Public License v3.0 | 4 votes |
render() { const columns = [{ title: '序号', dataIndex: 'id', key: 'id', render: (id, record, index) => { return index + 1; }, }, { title: '来源IP', dataIndex: 'clientIp', key: 'clientIp' }, { title: '接入方式', dataIndex: 'mode', key: 'mode', render: (text) => { return ( <Tag color={MODE_COLORS[text]}>{text}</Tag> ) } }, { title: '用户昵称', dataIndex: 'creatorName', key: 'creatorName' }, { title: '资产名称', dataIndex: 'assetName', key: 'assetName' }, { title: '连接协议', dataIndex: 'protocol', key: 'protocol', render: (text, record) => { const title = `${record.username}@${record.ip}:${record.port}`; return ( <Tooltip title={title}> <Tag color={PROTOCOL_COLORS[text]}>{text}</Tag> </Tooltip> ) } }, { title: '接入时间', dataIndex: 'connectedTime', key: 'connectedTime', render: (text, record) => { return ( <Tooltip title={text}> {dayjs(text).fromNow()} </Tooltip> ) } }, { title: '接入时长', dataIndex: 'connectedTime', key: 'connectedTime', render: (text, record) => { return differTime(new Date(record['connectedTime']), new Date(record['disconnectedTime'])); } }, { title: '操作', key: 'action', render: (text, record) => { let disabled = true; if (record['recording'] && record['recording'] === '1') { disabled = false } return ( <div> <Button type="link" size='small' disabled={disabled} onClick={() => this.showPlayback(record)}>回放</Button> <Button type="link" size='small' onClick={() => { confirm({ title: '您确定要禁止该IP访问本系统吗?', content: '', okText: '确定', okType: 'danger', cancelText: '取消', onOk: async () => { // 向后台提交数据 let formData = { ip: record['clientIp'], rule: 'reject', priority: 99, } const result = await request.post('/securities', formData); if (result.code === 1) { message.success('禁用成功'); } else { message.error(result.message, 10); } } }); }}>禁用IP</Button> <Button type="link" size='small' danger onClick={() => { confirm({ title: '您确定要删除此会话吗?', content: '', okText: '确定', okType: 'danger', cancelText: '取消', onOk: () => { this.del(record.id) } }); }}>删除</Button> </div> ) }, } ]; const selectedRowKeys = this.state.selectedRowKeys; const rowSelection = { selectedRowKeys: this.state.selectedRowKeys, onChange: (selectedRowKeys, selectedRows) => { this.setState({selectedRowKeys}); }, }; const hasSelected = selectedRowKeys.length > 0; const userOptions = this.state.users.map(d => <Select.Option key={d.id} value={d.id}>{d.nickname}</Select.Option>); const assetOptions = this.state.assets.map(d => <Select.Option key={d.id} value={d.id}>{d.name}</Select.Option>); return ( <> <Content className="site-layout-background page-content"> <div style={{marginBottom: 20}}> <Row justify="space-around" align="middle" gutter={24}> <Col span={4} key={1}> <Title level={3}>离线会话列表</Title> </Col> <Col span={20} key={2} style={{textAlign: 'right'}}> <Space> <Search ref={this.inputRefOfClientIp} placeholder="来源IP" allowClear onSearch={this.handleSearchByClientIp} /> <Select style={{width: 140}} showSearch value={this.state.queryParams.userId} placeholder='用户昵称' onSearch={this.handleSearchByNickname} onChange={this.handleChangeByUserId} filterOption={false} allowClear > {userOptions} </Select> <Select style={{width: 140}} showSearch value={this.state.queryParams.assetId} placeholder='资产名称' onSearch={this.handleSearchByAssetName} onChange={this.handleChangeByAssetId} filterOption={false} > {assetOptions} </Select> <Select onChange={this.handleChangeByRead} value={this.state.queryParams.reviewed ? this.state.queryParams.reviewed : ''} style={{width: 100}}> <Select.Option value="">全部会话</Select.Option> <Select.Option value="true">只看已读</Select.Option> <Select.Option value="false">只看未读</Select.Option> </Select> <Select onChange={this.handleChangeByProtocol} value={this.state.queryParams.protocol ? this.state.queryParams.protocol : ''} style={{width: 100}}> <Select.Option value="">全部协议</Select.Option> <Select.Option value="rdp">rdp</Select.Option> <Select.Option value="ssh">ssh</Select.Option> <Select.Option value="vnc">vnc</Select.Option> <Select.Option value="telnet">telnet</Select.Option> <Select.Option value="kubernetes">kubernetes</Select.Option> </Select> <Tooltip title='重置查询'> <Button icon={<UndoOutlined/>} onClick={() => { this.inputRefOfClientIp.current.setValue(''); this.loadTableData({ pageIndex: 1, pageSize: 10, protocol: '' }) }}> </Button> </Tooltip> <Divider type="vertical"/> <Tooltip title="刷新列表"> <Button icon={<SyncOutlined/>} onClick={() => { this.loadTableData(this.state.queryParams) }}> </Button> </Tooltip> <Tooltip title="全部标为已阅"> <Button icon={<CheckOutlined />} loading={this.state.reviewedAllBtnLoading} onClick={this.handleAllReviewed}> </Button> </Tooltip> <Tooltip title="标为已阅"> <Button disabled={!hasSelected} icon={<EyeOutlined />} loading={this.state.reviewedBtnLoading} onClick={this.handleReviewed}> </Button> </Tooltip> <Tooltip title="标为未阅"> <Button disabled={!hasSelected} icon={<EyeInvisibleOutlined />} loading={this.state.unreviewedBtnLoading} onClick={this.handleUnreviewed}> </Button> </Tooltip> <Tooltip title="批量删除"> <Button type="primary" danger disabled={!hasSelected} icon={<DeleteOutlined/>} loading={this.state.delBtnLoading} onClick={() => { const content = <div> 您确定要删除选中的<Text style={{color: '#1890FF'}} strong>{this.state.selectedRowKeys.length}</Text>条记录吗? </div>; confirm({ icon: <ExclamationCircleOutlined/>, content: content, onOk: () => { this.batchDelete() }, onCancel() { }, }); }}> </Button> </Tooltip> <Tooltip title="清空"> <Button type="primary" danger icon={<ClearOutlined/>} loading={this.state.clearBtnLoading} onClick={() => { const content = <Text style={{color: 'red'}} strong>您确定要清空全部的离线会话吗?</Text>; confirm({ icon: <ExclamationCircleOutlined/>, content: content, okType: 'danger', onOk: this.clearSession, onCancel() { }, }); }}> </Button> </Tooltip> </Space> </Col> </Row> </div> <Table rowSelection={rowSelection} dataSource={this.state.items} columns={columns} position={'both'} pagination={{ showSizeChanger: true, current: this.state.queryParams.pageIndex, pageSize: this.state.queryParams.pageSize, onChange: this.handleChangPage, total: this.state.total, showTotal: total => `总计 ${total} 条` }} loading={this.state.loading} rowClassName={(record, index) => { return record['reviewed'] ? '' : 'unreviewed'; }} /> { this.state.playbackVisible ? <Modal className='modal-no-padding' title={`会话回放 来源IP:${this.state.selectedRow['clientIp']} 用户昵称:${this.state.selectedRow['creatorName']} 资产名称:${this.state.selectedRow['assetName']} 网络:${this.state.selectedRow['username']}@${this.state.selectedRow['ip']}:${this.state.selectedRow['port']}`} centered={true} visible={this.state.playbackVisible} onCancel={this.hidePlayback} width={window.innerWidth * 0.8} footer={null} destroyOnClose maskClosable={false} > { this.state.selectedRow['mode'] === 'native' || this.state.selectedRow['mode'] === 'terminal' ? <iframe title='recording' style={{ width: '100%', // height: this.state.iFrameHeight, overflow: 'visible' }} onLoad={() => { // constant obj = ReactDOM.findDOMNode(this); // this.setState({ // "iFrameHeight": obj.contentWindow.document.body.scrollHeight + 'px' // }); }} ref="iframe" src={'./asciinema.html?sessionId=' + this.state.selectedRow['id']} width="100%" height={window.innerHeight * 0.8} frameBorder="0" /> : <Playback sessionId={this.state.selectedRow['id']}/> } </Modal> : undefined } </Content> </> ); }
Example #18
Source File: InstanceDetail.jsx From juno with Apache License 2.0 | 4 votes |
function InstanceDetail(props) {
const { currentInstance, dispatch, config, appName, env } = props;
const [visibleRestartModal, setVisibleRestartModal] = useState(false);
const [loading, setLoading] = useState(false);
const [content, setContent] = useState('');
if (!currentInstance) {
return <div />;
}
const {
config_file_used,
config_file_synced,
config_file_take_effect,
config_file_path,
sync_at,
version,
change_log,
host_name,
} = currentInstance;
let info = [
{
title: '接入状态',
help: '当前应用是否在该实例上接入配置中心',
content: config_file_used ? '已接入' : '未接入',
},
{
title: '发布状态',
help: '配置是否下发到机器上',
content: config_file_synced ? '已下发' : '未下发',
},
{
title: '生效状态',
help: '配置是否在应用上生效(依赖Go框架支持)',
content: config_file_take_effect ? '已生效' : '未生效',
},
{
title: '文件路径',
content: config_file_path
? config_file_path.split(';').map((item, idx) => {
return (
<div key={idx} className={styles.configPathItem}>
<span>{item}</span>
<CopyOutlined
onClick={() => {
copyToClipBoard(item);
message.success('已复制到剪切板');
}}
className={styles.configPathCopyBtn}
/>
</div>
);
})
: '---',
},
{
title: '配置版本',
content: version ? (
<span>
<Tag>{version}</Tag> {change_log}
</span>
) : (
'---'
),
},
{
title: '同步时间',
content: sync_at,
},
];
let showConfirm = (action, zoneCode, hostname, usedTyp) => {
const descMap = {
start: {
title: '确定启动应用进程吗?',
content: `应用进程会被执行 start 命令`,
},
restart: {
title: '确定重启应用进程吗?',
content: '应用进程会被执行 restart 命令',
},
stop: {
title: '确定停止应用进程吗?',
content: '应用进程会被执行 stop 命令',
},
};
const desc = descMap[action] || {};
confirm({
title: desc.title,
content: (
<div>
<p>{desc.content}</p>
<h4>操作实例:</h4>
<p>{hostname}</p>
</div>
),
onOk() {
setVisibleRestartModal(true);
doAction(action, zoneCode, hostname, usedTyp);
},
onCancel() {},
okText: '确定',
cancelText: '取消',
});
};
let doAction = (action, zoneCode, hostname, usedTyp) => {
if (usedTyp === 0) {
setContent('配置文件未接入,无法进行重启操作');
setLoading(false);
return;
}
setLoading(true);
ServiceAppAction({
action: action,
zone_code: zoneCode,
node_name: hostname,
typ: usedTyp,
app_name: appName,
env: env,
}).then((res) => {
if (res.code !== 0) {
setContent(res.data);
setLoading(false);
} else {
let result = [];
for (const itemListKey in res.data) {
let itemList = res.data[itemListKey];
if (itemList['code'] !== 0) {
result.push(<p>状态:重启失败</p>);
} else {
result.push(<p>状态:重启成功</p>);
}
for (const item in itemList) {
result.push(<p>{item + ':' + itemList[item]}</p>);
}
}
setContent(result);
setLoading(false);
}
});
};
return (
<div className={styles.instanceDetail}>
<div className={styles.topHeader}>
<div style={{ fontSize: '48px', textAlign: 'center', padding: '10px' }}>
<DatabaseOutlined />
</div>
<div style={{ padding: '10px' }}>
<div style={{ fontSize: '24px', fontWeight: 'bold' }}>{currentInstance.host_name}</div>
<div>
{currentInstance.region_name} {currentInstance.zone_name} {currentInstance.ip}
</div>
<div style={{ marginTop: '10px' }}>
<Space>
<Button
size={'small'}
type={'primary'}
icon={<ReloadOutlined />}
onClick={() => {
showConfirm(
'restart',
currentInstance.zone_code,
currentInstance.host_name,
currentInstance.config_file_used,
);
}}
>
重启
</Button>
<Button
size={'small'}
icon={<EyeOutlined />}
onClick={() => {
dispatch({
type: 'config/fetchInstanceConfig',
payload: {
id: config.id,
hostName: host_name,
},
});
}}
>
实时配置
</Button>
</Space>
</div>
</div>
</div>
<ul className={styles.instanceInfoList}>
{info.map((item, index) => (
<li key={index}>
<div className={styles.instanceInfoName}>
<div className={styles.instanceInfoTitle}>{item.title}</div>
<div className={styles.instanceInfoHelp}>{item.help}</div>
</div>
<div className={styles.instanceInfoContent}>{item.content}</div>
</li>
))}
</ul>
<Modal
title="操作面板"
visible={visibleRestartModal}
onOk={() => setVisibleRestartModal(false)}
onCancel={() => setVisibleRestartModal(false)}
>
<div>
<Spin spinning={loading} />
</div>
<div style={{ backgroundColor: 'black', borderRadius: '5px' }}>
<p style={{ color: 'green' }}>{content}</p>
</div>
</Modal>
<ModalRealtimeConfig />
</div>
);
}
Example #19
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 #20
Source File: adminChallenges.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><UploadOutlined/> Upload Challenges</span>} visible={this.state.uploadModalVisible} footer={null} onCancel={() => {this.setState({uploadModalVisible: false})}}>
<UploadChallengesForm handleRefresh={this.handleRefresh.bind(this)} closeUploadChallenges={() => {this.setState({uploadModalVisible: false})}}/>
</Modal>
<div style={{ display: (!this.state.challengeCreate && !this.state.editChallenge) ? "initial" : "none" }}>
<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={<FlagOutlined />} onClick={() => { this.setState({ challengeCreate: true }, this.props.history.push("/Admin/Challenges/Create")) }}>Create New Challenge</Button>
<Button type="primary" style={{ marginBottom: "2vh", marginRight: "1ch" }} icon={<UploadOutlined />} onClick={() => { this.setState({uploadModalVisible: true}) }}>Upload Challenges</Button>
{this.state.loading && (
<div style={{ display: "flex", justifyContent: "center", alignItems: "center" }}>
<Ellipsis color="#177ddc" size={60} />
<h1>Loading Challenges</h1>
</div>
)}
</div>
<Button loading={this.state.loading} type="primary" shape="circle" size="large" style={{ marginBottom: "2vh", maxWidth: "25ch" }} icon={<RedoOutlined />} onClick={async () => { await this.handleRefresh(); message.success("Challenge list refreshed.") }} />
</div>
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<div style={{ display: "flex", alignItems: "center" }}>
<Button disabled={this.state.disableEditButtons} type="default" style={{ marginBottom: "2vh", marginRight: "1ch", backgroundColor: "#6e6e6e" }} icon={<EyeOutlined style={{ color: "#49aa19" }} />} onClick={() => { this.editChallengeVisibility(true, this.state.selectedTableKeys, this.state.selectedRows) }}>Show</Button>
<Button disabled={this.state.disableEditButtons} type="default" style={{ marginBottom: "2vh", marginRight: "1ch", backgroundColor: "#6e6e6e" }} icon={<EyeInvisibleOutlined style={{ color: "#d32029" }} />} onClick={() => { this.editChallengeVisibility(false, this.state.selectedTableKeys, this.state.selectedRows) }}>Hide</Button>
<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 challenge(s) (' + this.state.selectedTableKeys.join(", ") + ')? This action is irreversible.',
icon: <ExclamationCircleOutlined />,
onOk: (close) => { this.deleteChallenge(close.bind(this), this.state.selectedTableKeys, this.state.selectedRows) },
onCancel: () => { },
});
}}>Delete Challenges</Button>
</div>
<div>
<Button disabled={this.state.disableEditButtons} type="primary" style={{ marginBottom: "2vh", marginRight: "1ch" }} icon={<DownloadOutlined />} onClick={() => { this.downloadChallenges(this.state.selectedRows) }}>Download</Button>
</div>
</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 Challenges Found/Created</h1>
</div>
)
}}>
<Column title="Name" dataIndex="name" key="name"
render={(text, row, index) => {
return <Link to={"/Challenges/" + row._id}><a style={{ fontWeight: 700 }}>{text}</a></Link>;
}}
filterDropdown={({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div style={{ padding: 8 }}>
<Input
autoFocus
placeholder="Search Challenge Name"
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => confirm()}
style={{ marginBottom: 8, display: 'block' }}
/>
<Space>
<Button
type="primary"
onClick={() => { confirm() }}
icon={<SearchOutlined />}
>
Search
</Button>
<Button onClick={() => clearFilters()}>
Reset
</Button>
</Space>
</div>
)}
onFilter={(value, record) => record.name.toLowerCase().trim().includes(value.toLowerCase())}
filterIcon={filtered => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />}
sorter={(a, b) => {
if (a.name < b.name) return -1
else return 1
}}
/>
<Column filters={this.state.allCat.map(value => { return { text: value.key, value: value.key } })} onFilter={(value, record) => value === record.category} title="Category" dataIndex="category" key="category" render={(text, row, index) => {
return <Link to={"/Challenges/" + row.category}><a style={{ fontWeight: 700 }}>{text}</a></Link>;
}} />
<Column sorter={(a, b) => a.points - b.points} title="Points" dataIndex="points" key="points" />
<Column sorter={(a, b) => a.points - b.points} title="Initial Points" dataIndex="initial" key="initial" />
<Column sorter={(a, b) => a.points - b.points} title="Solves to Min." dataIndex="minSolves" key="minSolves" />
<Column sorter={(a, b) => a.points - b.points} title="Min. Points" dataIndex="minimum" key="minimum" />
<Column filters={[{ text: "Visible", value: "true" }, { text: "Hidden", value: "false" }]} onFilter={(value, record) => { return value === record.visibility.props.visibility }} title="Visbility" dataIndex="visibility" key="visibility" />
<Column title="Required Challenge" dataIndex="requires" key="requires"
render={(text, row, index) => {
return <Link to={"/Challenges/" + text}><a style={{ fontWeight: 700 }}>{this.state.IDNameMapping[text]}</a></Link>;
}}
filterDropdown={({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div style={{ padding: 8 }}>
<Input
autoFocus
placeholder="Search Challenge Name"
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => confirm()}
style={{ marginBottom: 8, display: 'block' }}
/>
<Space>
<Button
type="primary"
onClick={() => { confirm() }}
icon={<SearchOutlined />}
>
Search
</Button>
<Button onClick={() => clearFilters()}>
Reset
</Button>
</Space>
</div>
)}
onFilter={(value, record) => { if (record.requires) return this.state.IDNameMapping[record.requires].toLowerCase().includes(value) }}
filterIcon={filtered => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />}
sorter={(a, b) => {
if (!a.requires) return -1
if (!b.requires) return 1
if (this.state.IDNameMapping[a.requires] < this.state.IDNameMapping[b.requires]) return -1
else return 1
}}
/>
<Column
title=""
key="edit"
render={(text, record) => (
<Button icon={<EditOutlined />} onClick={() => { this.setState({ editChallenge: true, id: record._id }); this.props.history.push("/Admin/Challenges/Edit") }}> Edit</Button>
)}
/>
</Table>
<Divider />
<div style={{ display: "flex", alignItems: "center" }}>
<h1 style={{ fontSize: "150%" }}>Category Management </h1>{this.state.transferDisabled && (<Ellipsis color="#177ddc" size={50} />)}
</div>
<Card className="settings-card">
<h3>Category Meta Information Editor <EyeOutlined /></h3>
<p>Select a category to edit info such as Name, Cover Pictures etc.</p>
<Select style={{ width: "30ch" }} value={this.state.categorySelect} onChange={this.openCategoryEditor.bind(this)}>
{this.state.categoryOptions}
</Select>
{this.state.currentEditCategory && (
<div style={{ padding: "10px", marginTop: "20px", backgroundColor: "rgba(0, 0, 0, 0.3)", border: "5px solid transparent", borderRadius: "10px" }}>
<EditCategoryForm initialData={this.state.currentEditCategory} handleEditCategoryDone={this.handleEditCategoryDone.bind(this)} />
</div>
)}
</Card>
<Card className="settings-card">
<h3>Category Visibility <EyeOutlined /></h3>
<Transfer
dataSource={this.state.allCat}
titles={[<span style={{ color: "#49aa19" }}>Visible Categories <EyeOutlined /></span>, <span style={{ color: "#d32029" }} >Hidden Categories <EyeInvisibleOutlined /></span>]}
targetKeys={this.state.targetKeys}
selectedKeys={this.state.selectedKeys}
onChange={this.handleChange}
onSelectChange={this.handleSelectChange}
render={item => item.key}
pagination
disabled={this.state.transferDisabled}
/>
</Card>
<Divider />
<div className="settings-responsive2" style={{ display: "flex", justifyContent: "space-around" }}>
<Card className="settings-card">
<h3>Disable Submissions: <AntdSwitch disabled={this.state.disableLoading} onClick={(value) => this.disableSetting("submissionDisabled", value)} checked={this.state.submissionDisabled} /></h3>
<p>Prevents users from submitting any new submissions for all challenges. Hints can still be bought</p>
</Card>
<Divider type="vertical" style={{ height: "inherit" }} />
<Card className="settings-card">
<h3>Set Socket Limit: <InputNumber
value={this.state.maxSockets}
disabled={this.state.disableLoading}
onChange={(value) => this.setState({ maxSockets: value })}
onPressEnter={(e) => { this.changeSetting("maxSockets", this.state.maxSockets) }} /></h3>
<p>Sets the maximum number of socket connections allowed <b>per account</b> to connect to the live scoreboard. <br /> <b>Press "Enter" to save</b></p>
</Card>
<Divider type="vertical" style={{ height: "inherit" }} />
<Card className="settings-card">
<h3>Disable First Blood for No Category: <AntdSwitch disabled={this.state.disableLoading} onClick={(value) => this.disableSetting("disableNonCatFB", value)} checked={this.state.disableNonCatFB} /></h3>
<p>Prevents people with no categories from attaining first blood. Useful if you want to limit First Blood prizes to only eligible participants.</p>
</Card>
</div>
</div>
<Switch>
<Route exact path='/Admin/Challenges/Create' render={(props) => <AdminChallengeCreate {...props} challenges={this.state.dataSource} handleBack={this.handleBack.bind(this)} handleCreateBack={this.handleCreateBack.bind(this)} allCat={this.state.allCat} />} />
<Route exact path='/Admin/Challenges/Edit' render={(props) => <AdminChallengeEdit {...props} allCat={this.state.allCat} IDNameMapping={this.state.IDNameMapping} challenges={this.state.dataSource} id={this.state.id} handleEditBack={this.handleEditBack.bind(this)} handleEditChallBack={this.handleEditChallBack.bind(this)} />} />
</Switch>
</Layout>
);
}
Example #21
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>
);
}