@chakra-ui/react#Select JavaScript Examples

The following examples show how to use @chakra-ui/react#Select. 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: Form.js    From web-client with Apache License 2.0 6 votes vote down vote up
TargetForm = ({ newTarget, onFormSubmit, targetSetter: setTarget }) => {
    const onFormChange = ev => {
        const target = ev.target;
        const name = target.name;
        let value = target.value;

        if ('tags' === name) {
            value = JSON.stringify(value.split(','));
        }

        setTarget({ ...newTarget, [name]: value });
    };

    return <div>
        <form onSubmit={onFormSubmit}>
            <FormControl id="name" isRequired>
                <FormLabel>Name</FormLabel>
                <Input name="name" placeholder="e.g. 127.0.0.1" onChange={onFormChange} isRequired autoFocus />
            </FormControl>
            <FormControl id="tags">
                <FormLabel>Tags</FormLabel>
                <Input name="tags" placeholder="e.g. linux,production" onChange={onFormChange} />
            </FormControl>
            <FormControl id="kind" isRequired>
                <FormLabel>Kind</FormLabel>
                <Select name="kind" onChange={onFormChange}>
                    {TargetKinds.map((targetKind, index) =>
                        <option key={index} value={targetKind.value}>{targetKind.description}</option>
                    )}
                </Select>
            </FormControl>
        </form>
    </div>
}
Example #2
Source File: Form.js    From web-client with Apache License 2.0 6 votes vote down vote up
VulnerabilityCategoryForm = ({ category, onFormSubmit, categorySetter: setCategory }) => {
    const [categories] = useFetch('/vulnerabilities/categories?parentsOnly=true');

    const onFormInputChange = ev => {
        const target = ev.target;
        const name = target.name;
        const value = target.value === "" ? null : target.value;

        setCategory({ ...category, [name]: value });
    };

    return <form id="vulnerability_category_form" onSubmit={onFormSubmit}>
        <FormControl id="parent_id" isRequired>
            <FormLabel>Parent category</FormLabel>
            {categories && <Select name="parent_id" value={category.parent_id} onChange={onFormInputChange}>
                <option>(none)</option>
                {categories.filter(category => category.parent_id === null).map(category => <option key={category.id} value={category.id}>{category.name}</option>)}
            </Select>}
        </FormControl>
        <FormControl id="name" isRequired>
            <FormLabel>Name</FormLabel>
            <Input name="name" autoFocus value={category.name} onChange={onFormInputChange} />
        </FormControl>
        <FormControl id="description">
            <FormLabel>Description</FormLabel>
            <Input name="description" value={category.description} onChange={onFormInputChange} />
        </FormControl>
    </form>
}
Example #3
Source File: Form.js    From web-client with Apache License 2.0 6 votes vote down vote up
DocumentForm = ({ document, onFormSubmit, documentSetter: setNote, isEditForm = false }) => {
    const onFormInputChange = ev => {
        const target = ev.target;
        const name = target.name;
        const value = target.value;

        setNote({
            ...document, [name]: value
        });
    };

    return <form onSubmit={onFormSubmit}>
        <FormControl isRequired>
            <FormLabel htmlFor="title">Title</FormLabel>
            <Input type="text" name="title" id="title" value={document.title || ""} onChange={onFormInputChange} autoFocus />
        </FormControl>

        <FormControl isRequired>
            <FormLabel htmlFor="content">Content (markdown supported)</FormLabel>
            <MarkdownEditor name="content" style={{ width: '100%' }} required value={document.content || ""}
                onChange={onFormInputChange} /><br />
        </FormControl>

        <FormControl>
            <FormLabel htmlFor="visibility">Visibility</FormLabel>
            <Select name="visibility" id="visibility" value={document.visibility} onChange={onFormInputChange} required>
                <option value="private">Private</option>
                <option value="public">Public</option>
            </Select>
        </FormControl>

        <PrimaryButton type="submit">{isEditForm ? "Update" : "Create"}</PrimaryButton>
    </form>
}
Example #4
Source File: Form.js    From web-client with Apache License 2.0 6 votes vote down vote up
NotesForm = ({ note, onFormSubmit, noteSetter: setNote }) => {
    const onFormInputChange = ev => {
        const target = ev.target;
        const name = target.name;
        const value = target.value;

        setNote({ ...note, [name]: value });
    };

    return <form onSubmit={onFormSubmit}>
        <FormControl id="content" isRequired>
            <FormLabel>Content</FormLabel>
            <Textarea name="content" style={{ width: '100%' }} value={note.content}
                onChange={onFormInputChange} autoFocus /><br />
        </FormControl>
        <FormControl id="visibility" isRequired>
            <FormLabel>Visibility</FormLabel>
            <Select name="visibility" value={note.visibility} onChange={onFormInputChange}>
                <option value="private">Private</option>
                <option value="public">Public</option>
            </Select>
        </FormControl>
    </form>
}
Example #5
Source File: Preferences.js    From web-client with Apache License 2.0 5 votes vote down vote up
UserPreferences = () => {

    const user = Auth.getLoggedInUser();
    user.preferences = initialiseUserPreferences(user);

    const timezones = CountriesTimezones.getAllTimezones();
    const timezoneKeys = Object.keys(timezones).sort();

    const { setTheme } = useContext(ThemeContext);

    const { setColorMode } = useColorMode();

    const [formValues, setFormValues] = useState({
        timezone: user.timezone,
        theme: user.preferences["web-client.theme"]
    });

    const updateFormValues = ev => {
        setFormValues({ ...formValues, [ev.target.name]: ev.target.value });
    }

    const onFormSubmit = ev => {
        ev.preventDefault();

        user.timezone = formValues.timezone;
        user.preferences = { ...initialiseUserPreferences(user), "web-client.theme": formValues.theme };

        secureApiFetch(`/users/${user.id}`, {
            method: 'PATCH',
            body: JSON.stringify({ timezone: formValues.timezone, preferences: user.preferences })
        })
            .then(() => {
                setTheme(theme => {
                    setThemeColors(formValues.theme);
                    setColorMode(formValues.theme);
                    return formValues.theme;
                });

                localStorage.setItem('user', JSON.stringify(user));

                actionCompletedToast("Your preferences have been saved.");
            })
            .catch(err => console.error(err));
    }

    return <>
        <PageTitle value="Preferences" />
        <div className='heading'>
            <Breadcrumb />
        </div>
        <Title type='User' title='Preferences' icon={<IconPreferences />} />
        <form onSubmit={onFormSubmit}>
            <FormControl>
                <FormLabel>Theme</FormLabel>
                <Select name="theme" onChange={updateFormValues} defaultValue={formValues.theme || "dark"}>
                    <option value="dark">Dark</option>
                    <option value="light">Light</option>
                </Select>
            </FormControl>
            <FormControl>
                <FormLabel>Timezone</FormLabel>
                <Select name="timezone" onChange={updateFormValues} defaultValue={user.timezone}>
                    {timezoneKeys.map((key, index) =>
                        <option key={index} value={timezones[key].name}>{timezones[key].name}</option>
                    )}
                </Select>
            </FormControl>

            <Primary type="submit">Save</Primary>
        </form>
    </>
}
Example #6
Source File: ExportForm.js    From web-client with Apache License 2.0 5 votes vote down vote up
ExportForm = () => {
    const [entities] = useFetch('/system/exportables');

    const [exportButtonDisabled, setExportButtonDisabled] = useState(true);

    const [entitiesToExport, setEntitiesToExport] = useState([]);

    const onEntitiesSelectionChange = ev => {
        const selectedEntities = Array.from(ev.target.selectedOptions, option => option.value);
        setExportButtonDisabled(selectedEntities.length === 0);
        setEntitiesToExport(selectedEntities);
    };

    const onExportButtonClick = ev => {
        ev.preventDefault();

        const url = `/system/data?` + new URLSearchParams({ entities: entitiesToExport }).toString();
        secureApiFetch(url, { method: 'GET' })
            .then(resp => {
                const contentDispositionHeader = resp.headers.get('Content-Disposition');
                const filenameRe = new RegExp(/filename="(.*)";/)
                const filename = filenameRe.exec(contentDispositionHeader)[1]
                return Promise.all([resp.blob(), filename]);
            })
            .then((values) => {
                const blob = values[0];
                const filename = values[1];
                const url = URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url;
                a.download = filename;
                a.click();
            })
    };

    return <div>
        <h3>Export system data</h3>

        <div style={{ marginTop: '5px', marginBottom: '5px' }}>
            Notes:
            <UnorderedList>
                <ListItem>Select one or more entities to export.</ListItem>
                <ListItem>The data will be returned in JSON format.</ListItem>
                <ListItem>This operation can take up to one minute to complete depending on the size of your database.</ListItem>
            </UnorderedList>
        </div>

        <Select multiple style={{ width: '80%', height: 250 }} onChange={onEntitiesSelectionChange}>
            {entities && entities.map(entity => <option key={entity.key} value={entity.key}>{entity.description}</option>)}
        </Select>
        <br />
        <PrimaryButton disabled={exportButtonDisabled}
            onClick={onExportButtonClick} leftIcon={<IconDownload />}>Export</PrimaryButton>
    </div>
}
Example #7
Source File: Send.js    From web-client with Apache License 2.0 5 votes vote down vote up
SendReport = () => {
    const navigate = useNavigate();
    const { projectId } = useParams();
    const [project] = useFetch(`/projects/${projectId}`)
    const [revisions] = useFetch(`/reports?projectId=${projectId}`)

    const [deliverySettings, setDeliverySettings] = useState({
        report_id: null,
        recipients: null,
        subject: "[CONFIDENTIAL] Security report attached",
        body: "Please review attachment containing a security report."
    })

    const handleSend = async (ev) => {
        ev.preventDefault();

        secureApiFetch(`/reports/${deliverySettings.report_id}/send`, { method: 'POST', body: JSON.stringify(deliverySettings) })
            .then(() => {
                navigate(`/projects/${project.id}/report`);
            })
            .catch(err => {
                console.error(err);
            })
    }

    const handleFormChange = ev => {
        const target = ev.target;
        const name = target.name;
        const value = target.value;
        setDeliverySettings({ ...deliverySettings, [name]: value });
    };

    useEffect(() => {
        if (revisions && deliverySettings.report_id === null)
            setDeliverySettings({ ...deliverySettings, report_id: revisions[0].id })
    }, [revisions, deliverySettings]);

    if (!project) return <Loading />

    return <div>
        <PageTitle value="Send report" />
        <div className='heading'>
            <Breadcrumb>
                <Link to="/projects">Projects</Link>
                {project && <Link to={`/projects/${project.id}`}>{project.name}</Link>}
                {project && <Link to={`/projects/${project.id}/report`}>Report</Link>}
            </Breadcrumb>
        </div>
        <form onSubmit={handleSend}>
            <Title title='Send report' />
            <FormControl isRequired>
                <FormLabel for="reportId">Revision</FormLabel>
                <Select id="reportId" name="report_id" onChange={handleFormChange}>
                    {revisions && revisions.map(revision => <option value={revision.id}>{revision.version_name}</option>)}
                </Select>
            </FormControl>
            <FormControl isRequired>
                <FormLabel>Recipients</FormLabel>
                <Input type="text" name="recipients" onChange={handleFormChange} autoFocus
                    placeholder="[email protected]" />
                <FormHelperText>Comma separated list of email addresses.</FormHelperText>
            </FormControl>
            <FormControl isRequired>
                <FormLabel>Subject</FormLabel>
                <Input type="text" name="subject" onChange={handleFormChange}
                    value={deliverySettings.subject} />
            </FormControl>
            <FormControl isRequired>
                <FormLabel>Body</FormLabel>
                <Textarea name="body" onChange={handleFormChange} value={deliverySettings.body} />
            </FormControl>

            <PrimaryButton type="submit">Send</PrimaryButton>
        </form>
    </div>
}
Example #8
Source File: Filters.js    From web-client with Apache License 2.0 5 votes vote down vote up
TaskFilters = ({ tableModel, tableModelSetter: setTableModel }) => {
    const loggedInUser = Auth.getLoggedInUser();
    const [projects] = useFetch('/projects');
    const [users] = useFetch('/users');

    const onFilterChange = ev => {
        setTableModel({ ...tableModel, filters: { ...tableModel.filters, [ev.target.name]: ev.target.value } });
    }

    return <><details>
        <summary>Filters</summary>
        <div className='space-x-2 mx-auto flex items-center'>
            <div>
                <label>Project</label>
                <Select name="projectId" onChange={onFilterChange}>
                    <option value="">(any)</option>
                    {null !== projects && projects.map(project => <option key={project.id} value={project.id}>{project.name}</option>)}
                </Select>
            </div>
            <div>
                <label>Priority</label>
                <Select name="priority" onChange={onFilterChange}>
                    <option value="">(any)</option>
                    {TaskPriorityList.map(priority => <option key={`priority_${priority.value}`} value={priority.value}>{priority.name}</option>)}
                </Select>
            </div>
            <div>
                <label>Assignee</label>
                <Select name="assigneeUid" onChange={onFilterChange}>
                    <option value="">(anybody)</option>
                    {null !== users && users.map(user => <option key={user.id} value={user.id}>{user.full_name}{user.id === loggedInUser.id ? " (You)" : ""}</option>)}
                </Select>
            </div>
            <div>
                <label>Status</label>
                <Select name="status" onChange={onFilterChange}>
                    <option value="">(any)</option>
                    {TaskStatuses.map(status => <option key={`taskstatus_${status.id}`} value={status.id}>{status.name}</option>)}
                </Select>
            </div>
        </div>
    </details>
    </>
}
Example #9
Source File: Form.js    From web-client with Apache License 2.0 5 votes vote down vote up
TaskForm = ({ isEditForm = false, forTemplate = false, onFormSubmit, task, taskSetter: setTask }) => {
    const [projects] = useFetch('/projects?isTemplate=' + (forTemplate ? 1 : 0));
    const [commands] = useFetch('/commands');

    const onFormChange = ev => {
        const target = ev.target;
        const name = target.name;
        const value = target.value;
        setTask({ ...task, [name]: value });
    };

    const initialCommand = { id: task.command_id, name: task.command_name };
    const [selectedCommand, setSelectedCommand] = useState(initialCommand);

    const onCommandChange = command => {
        setSelectedCommand(command);
        setTask({ ...task, command_id: command.id });
    }

    useEffect(() => {
        if (projects !== null && projects.length && task.project_id === "") {
            const newProjectId = projects[0].id;
            setTask(prevTask => ({ ...prevTask, project_id: newProjectId }));
        }
    }, [task.project_id, projects, setTask]);

    const loadOptions = (keywords) => {
        return secureApiFetch(`/commands?keywords=` + encodeURIComponent(keywords), { method: 'GET' }).then(data => data.json());
    }

    if (!commands) return <Loading />

    return <form onSubmit={onFormSubmit}>
        <label>
            Project
            <Select name="project_id" onChange={onFormChange}
                value={task.project_id} required>
                {projects && projects.map((project, index) =>
                    <option key={index} value={project.id}>{project.name}</option>
                )}
            </Select>
        </label>
        <label>Summary
            <Input type="text" name="summary" onChange={onFormChange} required autoFocus
                value={task.summary} /></label>
        <label>Description
            <MarkdownEditor name="description" onChange={onFormChange} required
                value={task.description || ''} /></label>
        <label>Priority
            <Select name="priority" onChange={onFormChange} value={task.priority || "medium"}>
                {TaskPriorityList.map(priority => <option value={priority.value}>{priority.name}</option>)}
            </Select>
        </label>
        <label>Due date
            <Input type="date" name="due_date" onChange={onFormChange} value={task.due_date} /></label>
        <label>Command
            <AsyncSelect name="command_id" value={selectedCommand} defaultOptions={true} loadOptions={loadOptions} getOptionLabel={opt => opt.name} getOptionValue={opt => opt.id} onChange={onCommandChange} isClearable />
        </label>

        <PrimaryButton type="submit">{isEditForm ? "Save" : "Create"}</PrimaryButton>
    </form>
}
Example #10
Source File: Form.js    From web-client with Apache License 2.0 5 votes vote down vote up
UserForm = ({ isEdit = false, user, userSetter: setUser, onFormSubmit }) => {

    const [passwordGenerationMethod, setPasswordGenerationMethod] = useState('autogenerated');

    const onFormChange = ev => {
        const target = ev.target;
        const name = target.name;
        const value = target.type === 'checkbox' ? target.checked : target.value;
        setUser({ ...user, [name]: value });
    };

    const onPasswordGenerationMethodChange = ev => {
        setPasswordGenerationMethod(ev.target.value);
    }

    return <form onSubmit={onFormSubmit} className="crud">
        <fieldset>
            <legend>Basic information</legend>

            <label>Full name
                <Input type="text" name="full_name" value={user.full_name || ""} onChange={onFormChange} required />
            </label>
            <label>Short bio
                <Input type="text" name="short_bio" value={user.short_bio || ""} onChange={onFormChange}
                    placeholder="DevSecOps, or Project Manager" />
            </label>
            <label>Email
                <Input type="email" name="email" value={user.email || ""} onChange={onFormChange} required />
            </label>
            <label>Role
                <Select name="role" onChange={onFormChange} value={user.role} required>
                    {UserRoles.map(role => <option key={`role_${role.id}`} value={role.id}>{role.name}</option>)}
                </Select>
            </label>
            <label>Properties
                <div>
                    <Checkbox name="active" isChecked={user.active} onChange={onFormChange}>Active</Checkbox>
                    <Checkbox name="mfa_enabled" isChecked={user.mfa_enabled} onChange={onFormChange}>2FA enabled</Checkbox>
                </div>
            </label>
        </fieldset>

        <fieldset>
            <legend>Credentials</legend>
            <label>Username
                <Input type="text" name="username" value={user.username || ""} onChange={onFormChange} autoFocus required />
            </label>
            {!isEdit &&
                <>
                    <label htmlFor="passwordGenerationMethod">Password generation method
                        <Select name="passwordGenerationMethod" onChange={onPasswordGenerationMethodChange}>
                            <option value="auto">Auto-generated</option>
                            <option value="manual">Manual</option>
                        </Select>
                    </label>
                    {passwordGenerationMethod === 'manual' &&
                        <>
                            <label>Password
                                <Input type="password" name="unencryptedPassword" onChange={onFormChange} required />
                            </label>
                            <label>
                                Send email to user
                                <Checkbox name="sendEmailToUser" onChange={onFormChange} />
                            </label>
                        </>
                    }
                </>
            }
        </fieldset>

        <PrimaryButton type="submit">{isEdit ? "Update" : "Create"}</PrimaryButton>
    </form >
}
Example #11
Source File: Filters.js    From web-client with Apache License 2.0 5 votes vote down vote up
VulnerabilityFilters = ({ tableModel, tableModelSetter: setTableModel, showProjectFilter = true }) => {
    const [categories] = useFetch('/vulnerabilities/categories?parentsOnly=true');
    const [projects] = useFetch('/projects?status=active');

    const onFilterChange = ev => {
        setTableModel({ ...tableModel, filters: { ...tableModel.filters, [ev.target.name]: ev.target.value } });
    }

    return <details>
        <summary>Filters</summary>
        <div className='space-x-2 mx-auto flex items-center'>
            {showProjectFilter && <div>
                <label>Project</label>
                <Select name="projectId" onChange={onFilterChange}>
                    <option value=''>(any)</option>
                    {projects !== null && projects.map(project => <option key={project.id} value={project.id}>{project.name}</option>)}
                </Select>
            </div>}

            <div>
                <label>Status</label>
                <Select name="status" onChange={onFilterChange}>
                    <option value=''>(any)</option>
                    <option value='open'>Open</option>
                    <option value='closed'>Closed</option>
                </Select>
            </div>

            <div>
                <label>Risk</label>
                <Select name="risk" onChange={onFilterChange}>
                    <option value=''>(any)</option>
                    {Risks.map(risk => <option key={risk.id} value={risk.id}>{risk.name}</option>)}
                </Select>
            </div>

            <div>
                <label>Category</label>
                <Select name="categoryId" onChange={onFilterChange}>
                    <option value=''>(any)</option>
                    {categories !== null && categories.map(category => <option key={category.id} value={category.id}>{category.name}</option>)}
                </Select>
            </div>
        </div>
    </details>
}
Example #12
Source File: texteditor.js    From GitMarkonics with MIT License 5 votes vote down vote up
render() {
    return (
        <div>
          <Flex color="black">
            <Box
                flex="1"
                bg="#F0A6CA"
                border="0.5px"
                borderColor="#F0E6EF"
                style={{ margin: 0, padding: "5px" }}
                p={1}
                m={2}
            >
              <Stack direction="row" spacing={1} align="center">
                <Select
                    onChange={this.handleHeadingChange.bind(this)}
                    width="50%"
                    bg="F0A6CA"
                    value={this.state.value}
                >
                  {headersMap.map((item, i) => (
                      <option value={item.style}>{item.label}</option>
                  ))}
                </Select>
                <Tooltip label={`Ctrl + ${shortcutKeyMap.BOLD} - Bold`}>
                  <Button onClick={this._onBoldClick.bind(this)}>
                    <Icon as={GoBold} />
                  </Button>
                </Tooltip>
                <Tooltip label={`Ctrl + ${shortcutKeyMap.ITALIC} - Italic`}>
                  <Button onClick={this._onItalicClick.bind(this)}>
                    {" "}
                    <Icon as={GoItalic} />
                  </Button>
                </Tooltip>
                <Tooltip label={`Ctrl + ${shortcutKeyMap.QUOTE} - Quote`}>
                  <Button onClick={this._onBlockQuoteClick.bind(this)}>
                    <Icon as={GoQuote} />
                  </Button>
                </Tooltip>
                <Tooltip label={`Ctrl + ${shortcutKeyMap.LIST} - List`}>
                  <Button onClick={this._onBulletClick.bind(this)}>
                    <Icon as={GoListUnordered} />
                  </Button>
                </Tooltip>
                {/* <Button onClick={this._onNumberClick.bind(this)}>
                <Icon as={GoListOrdered} /> */}
                {/* </Button> */}
                <Tooltip label={`Ctrl + ${shortcutKeyMap.DOWNLOAD} - Download`}>
                  <Button onClick={this.onDownload.bind(this)}>
                    <Icon as={GoDesktopDownload} /> &nbsp;Download
                  </Button>
                </Tooltip>
              </Stack>
              <Box className="editors-panel">
                <Editor
                    editorState={this.state.editorState}
                    handleKeyCommand={this.handleKeyCommand}
                    onChange={this.onChange}
                    placeholder="Lets Start Documenting ..."
                />
              </Box>
            </Box>
            <Output file={this.state.file} />
          </Flex>
        </div>
    );
  }
Example #13
Source File: List.js    From web-client with Apache License 2.0 4 votes vote down vote up
TasksList = () => {
    const navigate = useNavigate();

    const [tableModel, setTableModel] = useState(new TaskTableModel(true, true))

    const handleCreateTask = () => {
        navigate(`/tasks/create`);
    }

    const reloadTasks = useCallback(() => {
        setTableModel(tableModel => ({ ...tableModel, tasks: null }));

        const queryParams = new URLSearchParams();
        queryParams.set('orderColumn', tableModel.sortBy.column);
        queryParams.set('orderDirection', tableModel.sortBy.order);
        Object.keys(tableModel.filters)
            .forEach(key => tableModel.filters[key] !== null && tableModel.filters[key].length !== 0 && queryParams.append(key, tableModel.filters[key]));
        const url = `/tasks?${queryParams.toString()}`;

        secureApiFetch(url, { method: 'GET' })
            .then(resp => {
                return resp.json()
            })
            .then(data => {
                setTableModel(tableModel => ({ ...tableModel, tasks: data }));
            });
    }, [setTableModel, tableModel.filters, tableModel.sortBy.column, tableModel.sortBy.order]);

    const onStatusSelectChange = (ev) => {
        const newStatus = ev.target.value;

        secureApiFetch('/tasks', {
            method: 'PATCH',
            headers: {
                'Bulk-Operation': 'UPDATE',
            },
            body: JSON.stringify({
                taskIds: tableModel.selection,
                newStatus: newStatus
            })
        })
            .then(reloadTasks)
            .then(() => {
                actionCompletedToast(`All selected tasks have been transitioned to "${newStatus}".`);
                ev.target.value = '';
            })
            .catch(err => console.error(err));
    }

    const onDeleteButtonClick = () => {
        secureApiFetch('/tasks', {
            method: 'PATCH',
            headers: {
                'Bulk-Operation': 'DELETE',
            },
            body: JSON.stringify(tableModel.selection),
        })
            .then(reloadTasks)
            .then(() => {
                setTableModel({ ...tableModel, selection: [] })
                actionCompletedToast('All selected tasks were deleted.');
            })
            .catch(err => console.error(err));
    };

    const destroy = useDelete('/tasks/', reloadTasks);

    useEffect(() => {
        reloadTasks()
    }, [reloadTasks, tableModel.filters])

    return <>
        <PageTitle value="Tasks" />
        <div className='heading'>
            <Breadcrumb />
            <HStack alignItems='flex-end'>
                <CreateButton onClick={handleCreateTask}>Create task</CreateButton>
                <label>Transition to&nbsp;
                    <Select disabled={!tableModel.selection.length} onChange={onStatusSelectChange}>
                        <option value="">(select)</option>
                        {TaskStatuses.map((status, index) =>
                            <option key={index} value={status.id}>{status.name}</option>
                        )}
                    </Select>
                </label>
                <RestrictedComponent roles={['administrator']}>
                    <DeleteButton onClick={onDeleteButtonClick} disabled={!tableModel.selection.length}>
                        Delete selected
                    </DeleteButton>
                </RestrictedComponent>
            </HStack>
        </div>
        <Title title='Tasks' icon={<IconClipboardList />} />

        <Flex>
            <TaskFilters tableModel={tableModel} tableModelSetter={setTableModel} />
        </Flex>

        <TasksTable tableModel={tableModel} tableModelSetter={setTableModel} destroy={destroy} reloadCallback={reloadTasks} />
    </>
}
Example #14
Source File: index.js    From javascript-mini-projects with The Unlicense 4 votes vote down vote up
export default function Home({ category, quote, start }) {
	const cat = useRef();
	const [author, setAuthor] = useState(quote.author);
	const [quotes, setQuotes] = useState(quote.content);
	const [val, setVal] = useState(false);
	const [bgImage, setBgImage] = useState("white");
	const [fontCol, setFontCol] = useState("black");
	const { reset } = useIdleTimer({
		timeout: 1000 * 60 * 3,
		onIdle: handleOnIdle,
	});

	function handleOnIdle() {
		cat.current.value =
			category[Math.floor(Math.random() * category.length)];
		genQuote();
		reset();
	}

	async function genQuote() {
		const response = await axios.get(
			`https://api.quotable.io/random?tags=${cat.current.value}`
		);
		setAuthor(response.data.author);
		setQuotes(response.data.content);
		let x = Math.floor(Math.random() * 256);
		let y = Math.floor(Math.random() * 256);
		let z = Math.floor(Math.random() * 256);
		let bgColor = "rgb(" + x + "," + y + "," + z + ")";
		setBgImage(bgColor);

		let luminance = (0.299 * x + 0.587 * y + 0.114 * z) / 255;
		let d = 0;
		if (luminance > 0.5) d = 0;
		// bright colors - black font
		else d = 255; // dark colors - white font

		setFontCol(`rgb(${d},${d},${d})`);

		setVal(!val);
	}

	return (
		<>
			<Head>
				<title>Random Quote Generator</title>
				<meta
					name="description"
					content="Generated by create next app"
				/>
				<link rel="preconnect" href="https://fonts.googleapis.com" />
				<link
					rel="preconnect"
					href="https://fonts.gstatic.com"
					crossOrigin="true"
				/>
				<link
					href="https://fonts.googleapis.com/css2?family=Prompt:wght@300&display=swap"
					rel="stylesheet"
				/>
			</Head>
			<Box minH="100vh" w="100vw" zIndex="-1" position="fixed"></Box>
			<Flex
				flexDir="column"
				alignItems="center"
				justifyContent="center"
				w="100vw"
				bg={bgImage}
				minH="100vh"
			>
				<Heading my="3rem" fontSize="2rem" color={fontCol}>
					Random Quote Generator
				</Heading>
				<Flex
					justifyContent="center"
					bg="purple"
					w="100vw"
					mb="3rem"
					p="1rem"
				>
					<Flex
						alignItems="center"
						justifyContent="space-around"
						w="30%"
						minW="200px"
					>
						<Text as="strong" color="white">
							Select a category :
						</Text>
						<Select
							maxW="250px"
							textTransform="capitalize"
							ref={cat}
							defaultValue={start}
							variant="outline"
							color="black"
							bgColor="white"
						>
							{category &&
								category.map((item) => (
									<option value={item} key={item}>
										{item}
									</option>
								))}
							{!category && <Spinner size="lg" />}
						</Select>
					</Flex>
				</Flex>
				<Box
					w="50%"
					minW="250px"
					bg="gray.100"
					boxShadow="inset 2px 2px 8px gray"
					borderRadius="lg"
					p="3rem"
					fontSize="1.8rem"
					fontFamily="Prompt"
				>
					<Text as="blockquote" fontWeight="semibold">
						{quotes}
					</Text>
					<Text as="i" d="block" textAlign="right" mt="2rem">
						~ {author}
					</Text>
				</Box>
				<Button onClick={genQuote} colorScheme="facebook" mt="3rem">
					Get New Quote
				</Button>
			</Flex>
		</>
	);
}
Example #15
Source File: Details.js    From web-client with Apache License 2.0 4 votes vote down vote up
VulnerabilityDetails = () => {
    const navigate = useNavigate();
    const { vulnerabilityId } = useParams();
    const [vulnerability, updateVulnerability] = useFetch(`/vulnerabilities/${vulnerabilityId}`)
    const deleteVulnerability = useDelete(`/vulnerabilities/`)

    const parentType = 'vulnerability';
    const parentId = vulnerabilityId;
    const [attachments, reloadAttachments] = useFetch(`/attachments?parentType=${parentType}&parentId=${parentId}`)

    const handleDelete = async () => {
        const confirmed = await deleteVulnerability(vulnerabilityId);
        if (confirmed)
            navigate('/vulnerabilities')
    }

    const onStatusChange = ev => {
        const [status, substatus] = ev.target.value.split('-');
        secureApiFetch(`/vulnerabilities/${vulnerability.id}`, {
            method: 'PATCH',
            body: JSON.stringify({ status, substatus })
        })
            .then(() => {
                actionCompletedToast("The status has been transitioned.");
                updateVulnerability()
            })
            .catch(err => console.error(err))
    }

    if (!vulnerability) return <Loading />

    if (vulnerability && vulnerability.is_template) {
        return <Navigate to={`/vulnerabilities/templates/${vulnerability.id}`} />
    }

    return <div>
        <div className='heading'>
            <Breadcrumb>
                <Link to="/vulnerabilities">Vulnerabilities</Link>
            </Breadcrumb>
            <HStack alignItems='flex-end'>
                <RestrictedComponent roles={['administrator', 'superuser', 'user']}>
                    <EditButton onClick={(ev) => {
                        ev.preventDefault();
                        navigate(`/vulnerabilities/${vulnerability.id}/edit`)
                    }}>Edit</EditButton>

                    <label>Transition to&nbsp;
                        <Select onChange={onStatusChange} value={vulnerability.status + '-' + vulnerability.substatus}>
                            {VulnerabilityStatuses.map(status =>
                                <option key={`vulnstatus_${status.id}`} value={status.id}>{status.name}</option>
                            )}
                        </Select>
                    </label>

                    <DeleteButton onClick={handleDelete} />
                </RestrictedComponent>
            </HStack>
        </div>
        <article>
            <PageTitle value={`${vulnerability.summary} vulnerability`} />

            <Title type='Vulnerability' title={vulnerability.external_id ? <><strong>{vulnerability.external_id.toUpperCase()}</strong>&nbsp;{vulnerability.summary}</> : vulnerability.summary} icon={<IconFlag />} />
            <Tag size="sm" colorScheme="blue">{vulnerability.visibility}</Tag> <Tags values={vulnerability.tags} />

            <Tabs>
                <TabList>
                    <Tab>Description</Tab>
                    <Tab>Remediation</Tab>
                    <Tab>Notes</Tab>
                    <Tab>Attachments</Tab>
                </TabList>
                <TabPanels>
                    <TabPanel>
                        <VulnerabilityDescriptionPanel vulnerability={vulnerability} />
                    </TabPanel>
                    <TabPanel>
                        <VulnerabilityRemediationPanel vulnerability={vulnerability} />
                    </TabPanel>
                    <TabPanel>
                        <VulnerabilitiesNotesTab vulnerability={vulnerability} />
                    </TabPanel>
                    <TabPanel>
                        <AttachmentsDropzone parentType={parentType} parentId={parentId} onUploadFinished={reloadAttachments} />

                        <h4><IconDocument />Attachment list</h4>
                        <AttachmentsTable attachments={attachments} reloadAttachments={reloadAttachments} />
                    </TabPanel>
                </TabPanels>
            </Tabs>
        </article>

    </div >
}
Example #16
Source File: Details.js    From web-client with Apache License 2.0 4 votes vote down vote up
TaskDetails = () => {
    const loggedInUser = Auth.getLoggedInUser();
    const navigate = useNavigate();
    const { taskId } = useParams();
    const [task, fetchTask] = useFetch(`/tasks/${taskId}`)
    const [users] = useFetch(`/users`)
    const [project, setProject] = useState(null);
    const [command, setCommand] = useState(null);

    const parentType = 'task';
    const parentId = taskId;
    const [attachments, reloadAttachments] = useFetch(`/attachments?parentType=${parentType}&parentId=${parentId}`)

    const destroy = useDelete('/tasks/', fetchTask);

    const handleDelete = () => {
        destroy(task.id);
        navigate('/tasks');
    }

    const onAssigneeChange = ev => {
        const assigneeUid = ev.target.value;
        secureApiFetch(`/tasks/${task.id}`, {
            method: 'PATCH',
            body: JSON.stringify({ assignee_uid: '' === assigneeUid ? null : assigneeUid })
        })
            .then(() => {
                actionCompletedToast("The assignee has been updated.");
                fetchTask()
            })
            .catch(err => console.error(err))
    }

    const onStatusChange = ev => {
        const status = ev.target.value;
        secureApiFetch(`/tasks/${task.id}`, {
            method: 'PATCH',
            body: JSON.stringify({ status: status })
        })
            .then(() => {
                actionCompletedToast("The status has been transitioned.");
                fetchTask()
            })
            .catch(err => console.error(err))
    }

    useEffect(() => {
        if (task) {
            if (task.command_id) {
                secureApiFetch(`/commands/${task.command_id}`, { method: 'GET' })
                    .then(resp => resp.json())
                    .then(command => setCommand(command))
                    .catch(err => console.error(err))
            }

            secureApiFetch(`/projects/${task.project_id}`, { method: 'GET' })
                .then(resp => resp.json())
                .then(project => setProject(project))
                .catch(err => console.error(err))
        }
    }, [task])

    return <div>
        <div className="heading">
            <Breadcrumb>
                <Link to="/tasks">Tasks</Link>
                {project && <Link to={`/projects/${project.id}`}>{project.name}</Link>}
            </Breadcrumb>
            {task && users &&
                <HStack alignItems='flex-end'>
                    <RestrictedComponent roles={['administrator', 'superuser', 'user']}>
                        <Link to={`/tasks/${task.id}/edit`}>Edit</Link>
                        <label>Transition to&nbsp;
                            <Select onChange={onStatusChange} value={task.status}>
                                {TaskStatuses.map((status, index) =>
                                    <option key={index} value={status.id}>{status.name}</option>
                                )}
                            </Select>
                        </label>
                        <DeleteButton onClick={() => handleDelete(task.id)} />
                    </RestrictedComponent>
                </HStack>
            }
        </div>
        {!task ? <Loading /> :
            <article>
                <PageTitle value={`${task.summary} task`} />

                <Title title={task.summary} type='Task' icon={<IconClipboard />} />

                <Tabs>
                    <TabList>
                        <Tab>Details</Tab>
                        {null !== command && <Tab>Command instructions</Tab>}
                        <Tab>Attachments</Tab>
                    </TabList>
                    <TabPanels>
                        <TabPanel>
                            <div className="grid grid-two">
                                <div>
                                    <h4>Description</h4>
                                    {task.description ? <ReactMarkdown>{task.description}</ReactMarkdown> : <EmptyField />}
                                    <h4>Priority</h4>
                                    <p>{task.priority}</p>
                                    <h4>Status</h4>
                                    <p style={{ display: 'flex', alignItems: 'center', columnGap: 'var(--margin)' }}>
                                        <TaskStatusFormatter task={task} />
                                    </p>
                                    {task.command_id && <>
                                        <h4>Command</h4>
                                        <CommandBadge command={{ id: task.command_id, name: task.command_name }} />
                                    </>}
                                </div>

                                <div>
                                    <h4>People</h4>
                                    <dl>
                                        <dt>Created by</dt>
                                        <dd><UserLink userId={task.creator_uid}>{task.creator_full_name}</UserLink></dd>

                                        <dt>Assigned to</dt>
                                        <dd>
                                            {users &&
                                                <Select onChange={onAssigneeChange} defaultValue={task.assignee_uid}>
                                                    <option value="">(nobody)</option>
                                                    {users.map((user, index) =>
                                                        <option key={index} value={user.id}>{user.full_name}{user.id === loggedInUser.id ? " (You)" : ""}</option>
                                                    )}
                                                </Select>}
                                        </dd>
                                    </dl>

                                    <TimestampsSection entity={task} />
                                    {task.due_date &&
                                        <dl>
                                            <dt>Due date</dt>
                                            <dd><RelativeDateFormatter date={task.due_date} /></dd>
                                        </dl>
                                    }
                                </div>
                            </div>
                        </TabPanel>
                        {null !== command && <TabPanel>
                            <CommandInstructions command={command} task={task} />
                        </TabPanel>}
                        <TabPanel>
                            <AttachmentsDropzone parentType={parentType} parentId={parentId} onUploadFinished={reloadAttachments} />

                            <h4><IconDocument />Attachment list</h4>
                            <AttachmentsTable attachments={attachments} reloadAttachments={reloadAttachments} />
                        </TabPanel>
                    </TabPanels>
                </Tabs>
            </article>
        }
    </div>
}
Example #17
Source File: Form.js    From web-client with Apache License 2.0 4 votes vote down vote up
VulnerabilityForm = ({
    isEditForm = false,
    vulnerability,
    vulnerabilitySetter: setVulnerability,
    onFormSubmit
}) => {
    const [initialised, setInitialised] = useState(false);
    const [projects, setProjects] = useState(null);
    const [categories, setCategories] = useState(null);
    const [subCategories, setSubCategories] = useState(null);
    const [targets, setTargets] = useState(null);
    const [useOWASP, setMetrics] = useState(false);

    useEffect(() => {
        if (initialised) return;

        Promise.all([
            secureApiFetch(`/projects`, { method: 'GET' }),
            secureApiFetch(`/vulnerabilities/categories`, { method: 'GET' }),
        ])
            .then(resp => {
                const [respA, respB] = resp;
                return Promise.all([respA.json(), respB.json()]);
            })
            .then(([projects, categories]) => {
                const defaultProjectId = projects.length ? projects[0].id : 0;
                const projectId = isEditForm ? vulnerability.project_id : defaultProjectId;
                setMetrics(isOwaspProject(projects, projectId))

                var subcategories = null;
                if (vulnerability.parent_category_id) {
                    secureApiFetch(`/vulnerabilities/categories/${vulnerability.parent_category_id}`, { method: 'GET' })
                        .then(response => response.json())
                        .then(json => {
                            subcategories = json;
                        })
                }

                secureApiFetch(`/targets?projectId=${projectId}`, { method: 'GET' })
                    .then(resp => resp.json())
                    .then(targets => {
                        unstable_batchedUpdates(() => {
                            setProjects(projects);
                            setCategories(categories);
                            setTargets(targets);
                            setVulnerability(prevVulnerability => {
                                let updatedVulnerability = prevVulnerability;
                                if (!idExists(projects, prevVulnerability.project_id)) {
                                    updatedVulnerability.project_id = defaultProjectId;
                                }
                                if ((!idExists(categories, prevVulnerability.category_id)) && (!idExists(subcategories, prevVulnerability.category_id))) {
                                    updatedVulnerability.category_id = categories[0].id;
                                }
                                if (!idExists(targets, vulnerability.target_id)) {
                                    updatedVulnerability.target_id = null;
                                }
                                return updatedVulnerability;
                            })
                            setInitialised(true);
                        });
                    })
            });
    }, [initialised, isEditForm, setProjects, setCategories, setTargets, setMetrics, setVulnerability, vulnerability.target_id, vulnerability.project_id, vulnerability.parent_category_id, subCategories, setSubCategories]);

    useEffect(() => {
        if (!initialised) return;

        if (vulnerability.parent_category_id) {
            secureApiFetch(`/vulnerabilities/categories/${vulnerability.parent_category_id}`, { method: 'GET' })
                .then(response => response.json())
                .then(json => {
                    setSubCategories(json);
                })
        }

        const projectId = vulnerability.project_id;
        secureApiFetch(`/targets?projectId=${projectId}`, { method: 'GET' })
            .then(resp => resp.json())
            .then(targets => {
                unstable_batchedUpdates(() => {
                    setTargets(targets);
                    if (isEditForm) { // Edit
                        if (!idExists(targets, vulnerability.target_id)) {
                            setVulnerability(prevVulnerability => {
                                return { ...prevVulnerability, target_id: 0 }
                            });
                        }
                    }
                });
            })
    }, [initialised, isEditForm, setTargets, setVulnerability, vulnerability.target_id, vulnerability.project_id, vulnerability.parent_category_id]);

    const idExists = (elements, id) => {
        if (!elements) return false;
        for (const el of elements) {
            if (el.id === parseInt(id)) return true;
        }
        return false;
    }

    const isOwaspProject = (elements, id) => {
        let metrics = ProjectVulnerabilityMetrics[0].id;
        for (const el of elements) {
            if (el.id === parseInt(id)) {
                metrics = el.vulnerability_metrics;
            }
        }
        return (ProjectVulnerabilityMetrics[1].id === metrics);
    }

    const onFormChange = ev => {
        const target = ev.target;
        const name = target.name;
        let value = target.type === 'checkbox' ? target.checked : target.value;

        if ('tags' === name) {
            value = JSON.stringify(value.split(','));
        }

        if ('category_id' === name) {
            if (value !== '(none)') {
                secureApiFetch(`/vulnerabilities/categories/${value}`, { method: 'GET' })
                    .then(response => response.json())
                    .then(json => {
                        setSubCategories(json);
                    })
                setVulnerability({ ...vulnerability, 'parent_category_id': value, [name]: value });
            } else {
                setVulnerability({ ...vulnerability, 'category_id': null });
            }
        } else if ('subcategory_id' === name) {
            setVulnerability({ ...vulnerability, 'category_id': value });
        } else {
            setVulnerability({ ...vulnerability, [name]: value });
        }
    };

    return <form onSubmit={onFormSubmit} className="crud">
        <Accordion defaultIndex={0} allowToggle allowMultiple>
            <AccordionItem index={0}>
                <h2>
                    <AccordionButton>
                        <Box flex="1" textAlign="left">
                            Basic information
                        </Box>
                        <AccordionIcon />
                    </AccordionButton>
                </h2>
                <AccordionPanel pb={4}>
                    <label>Properties
                        <div>
                            <Checkbox name="is_template" onChange={onFormChange} isChecked={vulnerability.is_template}>Is template</Checkbox>
                        </div>
                    </label>
                    <label>External ID
                        <Input type="text" name="external_id" value={vulnerability.external_id || ""} onChange={onFormChange} />
                    </label>
                    <label>Summary
                        <Input type="text" name="summary" value={vulnerability.summary || ""} onChange={onFormChange} required autoFocus />
                    </label>
                    <label>Description
                        <MarkdownEditor name="description" value={vulnerability.description || ""} onChange={onFormChange} />
                    </label>
                    <label>External references
                        <MarkdownEditor name="external_refs" value={vulnerability.external_refs || ""} onChange={onFormChange} />
                    </label>
                    <label>Category
                        <Select name="category_id" value={vulnerability.parent_category_id || ""} onChange={onFormChange} required>
                            <option>(none)</option>
                            {categories && categories.map(cat =>
                                <option key={cat.id} value={cat.id}>{cat.name}</option>
                            )}
                        </Select>
                    </label>
                    <label>Subcategory
                        <Select name="subcategory_id" value={vulnerability.category_id || ""} onChange={onFormChange} required>
                            <option>(none)</option>
                            {subCategories && subCategories.map(subcat =>
                                <option key={subcat.id} value={subcat.id}>{subcat.name}</option>
                            )}
                        </Select>
                    </label>
                    <FormControl id="visibility" isRequired>
                        <FormLabel>Visibility</FormLabel>
                        <Select name="visibility" value={vulnerability.visibility || ""} onChange={onFormChange}>
                            <option value="public">Public</option>
                            <option value="private">Private</option>
                        </Select>
                        <FormHelperText>Private makes this vulnerability not visible to the client.</FormHelperText>
                    </FormControl>
                    <label>Risk
                        <Select name="risk" value={vulnerability.risk || ""} onChange={onFormChange} required>
                            {Risks.map(risk =>
                                <option key={risk.id} value={risk.id}>{risk.name}</option>
                            )}
                        </Select>
                    </label>
                    <label>Tags
                        <Input type="text" name="tags" onChange={onFormChange} value={vulnerability.tags ? JSON.parse(vulnerability.tags).join(',') : ''} />
                    </label>
                    <label>Proof of concept
                        <MarkdownEditor name="proof_of_concept" value={vulnerability.proof_of_concept || ""} onChange={onFormChange} />
                    </label>
                    <label>Impact
                        <MarkdownEditor name="impact" value={vulnerability.impact || ""} onChange={onFormChange} />
                    </label>
                    {
                        !useOWASP && <>
                            <label>CVSS score
                                <Input type="number" step="0.1" min="0" max="10" name="cvss_score" value={vulnerability.cvss_score || ""}
                                    onChange={onFormChange} />
                            </label>
                            <label><span><CvssAbbr /> vector</span>
                                <Input type="text" name="cvss_vector" value={vulnerability.cvss_vector || ""} onChange={onFormChange} placeholder="eg: AV:N/AC:L/Au:S/C:P/I:P/A:N" />
                            </label>
                        </>
                    }
                </AccordionPanel>
            </AccordionItem>
            {useOWASP &&
                <AccordionItem>
                    <h2>
                        <AccordionButton>
                            <Box flex="1" textAlign="left">
                                Owasp Risk Rating calculator
                            </Box>
                            <AccordionIcon />
                        </AccordionButton>
                    </h2>
                    <AccordionPanel pb={4}>
                        <label>Owasp Risk Rating</label>
                        <OwaspRR vulnerability={vulnerability} vulnerabilitySetter={setVulnerability} />
                    </AccordionPanel>
                </AccordionItem>
            }
            <AccordionItem>
                <h2>
                    <AccordionButton>
                        <Box flex="1" textAlign="left">
                            Remediation
                        </Box>
                        <AccordionIcon />
                    </AccordionButton>
                </h2>
                <AccordionPanel pb={4}>

                    <label>Remediation instructions
                        <MarkdownEditor name="remediation" value={vulnerability.remediation || ""} onChange={onFormChange} />
                    </label>
                    <label>Remediation complexity
                        <Select name="remediation_complexity" value={vulnerability.remediation_complexity || ""} onChange={onFormChange} required>
                            {RemediationComplexity.map(complexity =>
                                <option key={complexity.id} value={complexity.id}>{complexity.name}</option>
                            )}
                        </Select>
                    </label>
                    <label>Remediation priority
                        <Select name="remediation_priority" value={vulnerability.remediation_priority || ""} onChange={onFormChange} required>
                            {RemediationPriority.map(priority =>
                                <option key={priority.id} value={priority.id}>{priority.name}</option>
                            )}
                        </Select>
                    </label>
                </AccordionPanel>
            </AccordionItem>

            {
                !vulnerability.is_template && <AccordionItem>
                    <h2>
                        <AccordionButton>
                            <Box flex="1" textAlign="left">
                                Relations
                            </Box>
                            <AccordionIcon />
                        </AccordionButton>
                    </h2>
                    <AccordionPanel pb={4}>

                        <label>Project
                            <Select name="project_id" value={vulnerability.project_id || ""} onChange={onFormChange} required>
                                {projects && projects.map((project, index) =>
                                    <option key={index} value={project.id}>{project.name}</option>
                                )}
                            </Select>
                        </label>

                        <label>Affected target
                            <Select name="target_id" value={vulnerability.target_id || ""} onChange={onFormChange}>
                                <option value="0">(none)</option>
                                {targets && targets.map((target, index) =>
                                    <option key={index} value={target.id}>{target.name}</option>
                                )}
                            </Select>
                        </label>
                    </AccordionPanel>
                </AccordionItem>
            }
        </Accordion>

        <Primary type="submit">{isEditForm ? "Save" : "Add"}</Primary>
    </form >
}
Example #18
Source File: ModalDialog.js    From web-client with Apache License 2.0 4 votes vote down vote up
ReportVersionModalDialog = ({ projectId, isOpen, onSubmit, onCancel }) => {
    const defaultFormValues = { reportTemplateId: 0, name: "", description: "" };
    const [formValues, setFormValues] = useState(defaultFormValues);
    const [templates] = useFetch('/reports/templates');

    const onFormValueChange = ev => {
        ev.preventDefault();

        setFormValues({ ...formValues, [ev.target.name]: ev.target.value });
    };

    const beforeCancelCallback = ev => {
        setFormValues(defaultFormValues);
        onCancel(ev);
    }

    const onFormSubmit = ev => {
        ev.preventDefault();

        const params = {
            projectId: projectId,
            reportTemplateId: formValues.reportTemplateId,
            name: formValues.name,
            description: formValues.description
        };

        secureApiFetch(`/reports`, { method: 'POST', body: JSON.stringify(params) })
            .then(() => {
                onSubmit();
                actionCompletedToast(`The report version "${formValues.name}" has been added.`);
            })
            .catch(err => {
                console.error(err);
            })
            .finally(() => {
                setFormValues(defaultFormValues)
            })
    }

    useEffect(() => {
        if (templates !== null && templates.length > 0) {
            setFormValues((prev) => ({ ...prev, reportTemplateId: templates[0].id }))
        }
    }, [templates]);

    return <Modal size="xl" isOpen={isOpen} onClose={beforeCancelCallback}>
        <ModalOverlay />
        <ModalContent>
            <ModalHeader><HStack><TargetIcon style={{ width: '24px' }} /> <h4>New report version details</h4></HStack></ModalHeader>
            <ModalCloseButton />
            <ModalBody>
                <form id="reportVersionReportForm" onSubmit={onFormSubmit} className="crud" style={{ marginTop: '20px' }}>
                    <FormControl isRequired>
                        <FormLabel>Template</FormLabel>
                        {templates && <Select name="reportTemplateId" value={formValues.reportTemplateId} onChange={onFormValueChange}>
                            {templates.map(template => <option key={template.id} value={template.id}>{template.version_name}</option>)}
                        </Select>}
                    </FormControl>

                    <FormControl isRequired>
                        <FormLabel>Name</FormLabel>
                        <Input type="text" name="name" value={formValues.name} onChange={onFormValueChange}
                            placeholder="eg 1.0, 202103" autoFocus />
                    </FormControl>

                    <FormControl isRequired>
                        <FormLabel>Description</FormLabel>
                        <Input type="text" name="description" value={formValues.description}
                            onChange={onFormValueChange}
                            placeholder="eg Initial version, Draft"
                        />
                    </FormControl>
                </form>
            </ModalBody>

            <ModalFooter>
                <Button onClick={beforeCancelCallback} mr={3}>Cancel</Button>
                <Button form="reportVersionReportForm" type="submit" colorScheme="blue">Save</Button>
            </ModalFooter>
        </ModalContent>
    </Modal>
}
Example #19
Source File: VaultTab.js    From web-client with Apache License 2.0 4 votes vote down vote up
ProjectVaultTab = ({ project }) => {
    const [vault, refreshVault] = useFetch(`/vault/${project.id}`);
    const [vaultItem, setVaultItem] = useState(new Vault());

    const onVaultItemFormChange = ev => {
        const value = ev.target.type === 'checkbox' ? ev.target.checked : ev.target.value;
        setVaultItem({ ...vaultItem, [ev.target.name]: value });
    }

    const onVaultItemDelete = vaultItemId => {
        secureApiFetch(`/vault/${project.id}/${vaultItemId}`, { method: 'DELETE' })
            .then(() => {
                refreshVault();
                actionCompletedToast("The vault item has been deleted.");
            })
            .catch(err => console.error(err))
    }

    const onFormSubmit = ev => {
        ev.preventDefault();

        secureApiFetch(`/vault/${project.id}`, { method: 'POST', body: JSON.stringify(vaultItem) })
            .then(resp => {
                if (resp.status === 201) {
                    setVaultItem(new Vault());
                    refreshVault();
                    actionCompletedToast(`The vault item has been added.`);
                } else {
                    errorToast("The vault item could not be saved. Review the form data or check the application logs.")
                }
            })
    }

    return <section>
        <RestrictedComponent roles={['administrator', 'superuser', 'user']} message="(access restricted)">
            {vault && <>
                <Table>
                    <Thead>
                        <Tr>
                            <Th>Name</Th>
                            <Th>Note</Th>
                            <Th>Type</Th>
                            <Th>Reportable</Th>
                            <Th>&nbsp;</Th>
                        </Tr>
                    </Thead>
                    <Tbody>
                        {0 === vault.length && <NoResultsTableRow numColumns={3} />}
                        {vault.map(item => <>
                            <Tr key={item.id}>
                                <Td>{item.name}</Td>
                                <Td>{item.note}</Td>
                                <Td>{item.type}</Td>
                                <Td>{item.reportable}</Td>
                                <Td textAlign="right">
                                    <LinkButton href={`/vault/${project.id}/${item.id}/edit`}>Edit</LinkButton>
                                    <DeleteIconButton onClick={onVaultItemDelete.bind(this, item.id)} />
                                </Td>
                            </Tr>
                        </>)}
                    </Tbody>
                </Table>
                <form onSubmit={onFormSubmit}>
                    <h3>New vault item</h3>
                    <Table>
                        <Thead>
                            <Tr>
                                <Th>Type</Th>
                                <Th>Name</Th>
                                <Th>Note</Th>
                                <Th>Value</Th>
                                <Th>Password</Th>
                                <Th>Reportable</Th>
                                <Th>&nbsp;</Th>
                            </Tr>
                        </Thead>
                        <Tbody>
                            <Tr>
                                <Td>
                                    <Select name="type" onChange={onVaultItemFormChange} value={vaultItem.type || ""} isRequired>
                                        <option value="password">Password</option>
                                        <option value="note">Note</option>
                                        <option value="token">Token</option>
                                        <option value="key">Key</option>
                                    </Select>
                                </Td>
                                <Td>
                                    <Input type="text" name="name" onChange={onVaultItemFormChange} value={vaultItem.name || ""} isRequired />
                                </Td>
                                <Td>
                                    <Input type="text" name="note" onChange={onVaultItemFormChange} value={vaultItem.note || ""} />
                                </Td>
                                <Td>
                                    <Input type="text" name="value" onChange={onVaultItemFormChange} value={vaultItem.value || ""} isRequired />
                                </Td>
                                <Td>
                                    <Input type="password" name="password" onChange={onVaultItemFormChange} value={vaultItem.password || ""} isRequired />
                                </Td>
                                <Td>
                                    <Checkbox name="reportable" onChange={onVaultItemFormChange} isChecked={vaultItem.reportable} />
                                </Td>
                                <Td>
                                    <Button type="submit">Add</Button>
                                </Td>
                            </Tr>
                        </Tbody>
                    </Table>
                </form>
            </>}
        </RestrictedComponent>
    </section>;
}
Example #20
Source File: VaultItemEdit.js    From web-client with Apache License 2.0 4 votes vote down vote up
VaultItemEdit = () => {
    const { projectId, vaultItemId } = useParams();
    const navigate = useNavigate();

    const [item, setVaultItem] = useState(new Vault());
    const [password, setPassword] = useState(null);

    const onVaultItemFormChange = ev => {
        const value = ev.target.type === 'checkbox' ? ev.target.checked : ev.target.value;
        setVaultItem({ ...item, [ev.target.name]: value });
    }

    const onFormSubmit = ev => {
        ev.preventDefault();

        item.password = password;

        secureApiFetch(`/vault/${projectId}/${vaultItemId}`, { method: 'PUT', body: JSON.stringify(item) })
            .then(resp => {
                if (resp.status === 201) {
                    setVaultItem(new Vault());
                    setPassword(null);
                    actionCompletedToast(`The vault item has been modified.`);
                    navigate(`/projects/${projectId}`);
                } else {
                    errorToast("The vault item could not be saved. Review the form data or check the application logs.")
                }
            })
    }

    const onPasswordProvided = ev => {
        ev.preventDefault();

        secureApiFetch(`/vault/${projectId}/${vaultItemId}`, { method: 'POST', body: JSON.stringify({ 'password': password }) })
            .then(response => response.json())
            .then(json => {
                if (json['success'] === false) {
                    errorToast("Seems like a wrong password.");
                    setPassword(null);
                }
                else {
                    var newItem = new Vault();
                    newItem.name = json['name'];
                    newItem.note = json['note'];
                    newItem.value = json['value'];
                    newItem.type = json['type'];
                    newItem.reportable = json['reportable'];
                    setVaultItem(newItem);
                    actionCompletedToast(`The vault item "${newItem.name}" has been loaded.`);
                }
            })
            .catch(err => {
                errorToast(err);
                setPassword(null);
            })
    }

    const onPasswordFormChanged = ev => {
        setPassword(ev.target.value);
    }

    return <div>
        {item.name !== "" && <>
            <form onSubmit={onFormSubmit}>
                <h3>Vault item</h3>
                <Table>
                    <Thead>
                        <Tr>
                            <Th>Type</Th>
                            <Th>Name</Th>
                            <Th>Note</Th>
                            <Th>Value</Th>
                            <Th>Reportable</Th>
                            <Th>&nbsp;</Th>
                        </Tr>
                    </Thead>
                    <Tbody>
                        <Tr>
                            <Td>
                                <Select name="type" onChange={onVaultItemFormChange} value={item.type || ""} isRequired>
                                    <option value="password">Password</option>
                                    <option value="note">Note</option>
                                    <option value="token">Token</option>
                                    <option value="key">Key</option>
                                </Select>
                            </Td>
                            <Td>
                                <Input type="text" name="name" onChange={onVaultItemFormChange} value={item.name || ""} isRequired />
                            </Td>
                            <Td>
                                <Input type="text" name="note" onChange={onVaultItemFormChange} value={item.note || ""} />
                            </Td>
                            <Td>
                                <Input type="text" name="value" onChange={onVaultItemFormChange} value={item.value || ""} isRequired />
                            </Td>
                            <Td>
                                <Checkbox name="reportable" onChange={onVaultItemFormChange} isChecked={item.reportable} />
                            </Td>
                            <Td>
                                <Button type="submit">Update</Button>
                            </Td>
                        </Tr>
                    </Tbody>
                </Table>
            </form>
        </>}
        {item.name === "" && <>
            <h3>Please provide password</h3>
            <form onSubmit={onPasswordProvided}>
                <Input type="password" name="password" onChange={onPasswordFormChanged} value={password || ""} isRequired />
                <Button type="submit">Send</Button>
            </form>
        </>}
    </div>
}
Example #21
Source File: Membership.js    From web-client with Apache License 2.0 4 votes vote down vote up
ProjectMembership = () => {
    const { projectId } = useParams();
    const [users] = useFetch(`/users`)
    const [members, updateMembers] = useFetch(`/projects/${projectId}/users`)
    const [savedProject] = useFetch(`/projects/${projectId}`);
    const [availableUsers, setAvailableUsers] = useState([]);

    const handleOnClick = ev => {
        ev.preventDefault();

        const userId = document.getElementById('userId').value;
        const userData = { userId: userId };
        secureApiFetch(`/projects/${projectId}/users`, {
            method: 'POST',
            body: JSON.stringify(userData)
        }).then(() => {
            updateMembers()
        })
    }

    const handleDelete = (member) => {
        secureApiFetch(`/projects/${projectId}/users/${member.membership_id}`, {
            method: 'DELETE'
        }).then(() => {
            updateMembers()
        })
    }

    useEffect(() => {
        if (members && users && users.length > 0) {
            const memberIds = members.reduce((list, user) => [...list, user.id], []);
            setAvailableUsers(users.filter(user => !memberIds.includes(user.id)));
        }
    }, [members, users]);

    return <div>
        <PageTitle value="Project membership" />
        <div className="heading">
            <Breadcrumb>
                <Link to="/projects">Projects</Link>
                {savedProject && <Link to={`/projects/${savedProject.id}`}>{savedProject.name}</Link>}
            </Breadcrumb>
        </div>

        <Title title='Members' />

        {availableUsers.length > 0 ?
            <form>
                <label>
                    Select user
                    <Select id="userId">
                        {availableUsers && availableUsers.map((user, index) =>
                            <option key={index} value={user.id}>{user.full_name}</option>
                        )}
                    </Select>
                </label>
                <PrimaryButton onClick={handleOnClick} leftIcon={<IconPlus />}>Add as member</PrimaryButton>
            </form> :
            <Alert status="info">
                <AlertIcon />
                All users have been added to the project.
            </Alert>
        }

        <Table>
            <Thead>
                <Tr>
                    <Th style={{ width: '80px' }}>&nbsp;</Th>
                    <Th>Name</Th>
                    <Th>Role</Th>
                    <Th>&nbsp;</Th>
                </Tr>
            </Thead>
            <Tbody>
                {null === members &&
                    <LoadingTableRow numColumns={4} />}
                {null !== members && 0 === members.length &&
                    <NoResultsTableRow numColumns={4} />}
                {members && members.map((member, index) =>
                    <Tr key={index}>
                        <Td><UserAvatar email={member.email} /></Td>
                        <Td><UserLink userId={member.id}>{member.full_name}</UserLink></Td>
                        <Td><UserRoleBadge role={member.role} /></Td>
                        <Td textAlign="right">
                            <DeleteIconButton onClick={() => handleDelete(member)} />
                        </Td>
                    </Tr>
                )
                }
            </Tbody>
        </Table>
    </div>
}
Example #22
Source File: List.js    From web-client with Apache License 2.0 4 votes vote down vote up
ProjectsList = () => {
    const navigate = useNavigate();
    const query = useQuery();
    let pageNumber = query.get('page');
    pageNumber = pageNumber !== null ? parseInt(pageNumber) : 1;
    const apiPageNumber = pageNumber - 1;

    const [numberPages, setNumberPages] = useState(1);
    const [totalCount, setTotalCount] = useState('?');

    const [projects, setProjects] = useState([]);
    const [statusFilter, setStatusFilter] = useState('active');

    const handleCreateProject = () => {
        navigate('/projects/create')
    }

    const onStatusFilterChange = ev => {
        setStatusFilter(ev.target.value);
    }

    const onPageChange = pageNumber => {
        const queryParams = new URLSearchParams();
        queryParams.set('page', pageNumber + 1);
        const url = `/projects?${queryParams.toString()}`;
        navigate(url);
    }

    const reloadProjects = useCallback(() => {
        setProjects(null);

        const queryParams = new URLSearchParams();
        queryParams.set('page', apiPageNumber);
        if (statusFilter.length) {
            queryParams.set('status', statusFilter);
        }
        const url = `/projects?${queryParams.toString()}`;

        secureApiFetch(url)
            .then(resp => {
                if (resp.headers.has('X-Page-Count')) {
                    setNumberPages(resp.headers.get('X-Page-Count'))
                }
                if (resp.headers.has('X-Total-Count')) {
                    setTotalCount(resp.headers.get('X-Total-Count'))
                }
                return resp.json()
            })
            .then(projects => {
                setProjects(projects);
            });
    }, [apiPageNumber, statusFilter])

    const destroy = useDelete('/projects/', reloadProjects);

    useEffect(() => {
        reloadProjects();
    }, [statusFilter, reloadProjects]);

    return <div>
        <PageTitle value="Projects" />
        <div className='heading'>
            <Breadcrumb />
            <PaginationV2 page={apiPageNumber} total={numberPages} onPageChange={onPageChange} />

            <HStack>
                <RestrictedComponent roles={['administrator', 'superuser', 'user']}>
                    <CreateButton onClick={handleCreateProject}>Create project</CreateButton>
                </RestrictedComponent>
            </HStack>
        </div>
        <Title title={`Projects (${totalCount})`} icon={<IconFolder />} />

        <Flex>
            <details>
                <summary>Filters</summary>
                <div className='space-x-2 mx-auto flex items-center'>
                    <div>
                        <label>Status
                            <Select onChange={onStatusFilterChange} defaultValue="active">
                                <option value="">(any)</option>
                                <option value="active">Active</option>
                                <option value="archived">Archived</option>
                            </Select>
                        </label>
                    </div>
                </div>
            </details>
        </Flex>
        <ProjectsTable projects={projects} destroy={destroy} />
    </div>
}
Example #23
Source File: Form.js    From web-client with Apache License 2.0 4 votes vote down vote up
ProjectForm = ({ isEdit = false, project, projectSetter: setProject, onFormSubmit }) => {

    const [clients] = useFetch('/clients');
    const [categories] = useFetch('/project/categories');

    const handleFormChange = ev => {
        const value = ev.target.type === 'checkbox' ? ev.target.checked : ev.target.value;
        setProject({ ...project, [ev.target.name]: value });
    };

    useEffect(() => {
        if (clients && clients.length && (project.client_id === null || project.client_id === 0)) {
            setProject({ ...project, client_id: clients[0].id });
        }
    }, [project, clients, setProject]);

    if (!project && !clients) return <Loading />

    return <form onSubmit={onFormSubmit} className="crud">

        <fieldset>
            <legend>Basic information</legend>

            <label>Is template?
                <Checkbox name="is_template" onChange={handleFormChange} isChecked={project.is_template} />
            </label>

            <label>
                Category
                <Select name="category_id" value={project.category_id || ""} onChange={handleFormChange} required>
                    {categories && <>
                        <option value="">(undefined)</option>
                        {categories.map(category => <option key={`category_${category.id}`} value={category.id}>{category.name}</option>)}
                    </>}
                </Select>
            </label>

            {!project.is_template && <>
                <label>Visibility
                    <Select name="visibility" onChange={handleFormChange} value={project.visibility}>
                        <option value="public">Public</option>
                        <option value="private">Private</option>
                    </Select>
                </label>

                <label>Client
                    <Select name="client_id" onChange={handleFormChange} value={project.client_id || ""}>
                        {clients && clients.map((client, index) =>
                            <option key={index} value={client.id}>{client.name}</option>
                        )}
                    </Select>
                </label>

                <label>External ID
                    <Input type="text" name="external_id" onChange={handleFormChange} value={project.external_id || ""} />
                </label>
            </>}

            <label>Name
                <Input type="text" name="name" onChange={handleFormChange} value={project.name || ""} required autoFocus />
            </label>
            <label>Description
                <MarkdownEditor name="description" onChange={handleFormChange} value={project.description || ""} required />
            </label>
        </fieldset>

        <fieldset>
            <legend>Rules of engagement</legend>

            <label>
                Vulnerability metrics
                <Select name="vulnerability_metrics" value={project.vulnerability_metrics || ""} onChange={handleFormChange}>
                    <option value="">(undefined)</option>
                    {ProjectVulnerabilityMetrics.map(type => <option key={`metrics_${type.id}`} value={type.id}>{type.name}</option>)}
                </Select>
            </label>

            {!project.is_template && <>
                <label>Start date
                    <Input type="date" name="engagement_start_date" value={project.engagement_start_date}
                        onChange={handleFormChange} />
                </label>

                <label>End date
                    <Input type="date" name="engagement_end_date" value={project.engagement_end_date}
                        onChange={handleFormChange} />
                </label>
            </>}
        </fieldset>

        <fieldset>
            <legend>Vulnerabilities summary</legend>

            <label>Management summary
                <MarkdownEditor name="management_summary" onChange={handleFormChange} value={project.management_summary || ""} required />
            </label>

            <label>Conclusion
                <MarkdownEditor name="management_conclusion" onChange={handleFormChange} value={project.management_conclusion || ""} required />
            </label>
        </fieldset>

        <PrimaryButton type="submit">{isEdit ? "Update" : "Create"}</PrimaryButton>
    </form>
}
Example #24
Source File: Form.js    From web-client with Apache License 2.0 4 votes vote down vote up
CommandForm = ({ isEditForm = false, onFormSubmit, command, commandSetter: setCommand }) => {
    const onFormChange = ev => {
        const target = ev.target;
        let name = target.name;
        let value = target.value;
        if ('tags' === name) {
            value = JSON.stringify(value.split(','));
        }
        if ('output_capture' === name) {
            const outputFileName = value === 'disabled' ? null :
                value === 'stdout' ? '{{{STDOUT}}}' :
                    "";

            setCommand({ ...command, [name]: value, output_filename: outputFileName });
        } else {
            setCommand({ ...command, [name]: value });
        }
    };

    const [parsers] = useFetch('/commands/output-parsers');

    if (!parsers) return <Loading />

    return <form onSubmit={onFormSubmit} className="crud">
        <fieldset>
            <legend>Basic information</legend>
            <label>Name
                <Input type="text" name="name" onChange={onFormChange} value={command.name || ""} required autoFocus /></label>
            <label>Description
                <MarkdownEditor name="description" onChange={onFormChange} value={command.description || ""} required />
            </label>
            <label>Tags
                <Input type="text" name="tags" onChange={onFormChange} value={command.tags ? JSON.parse(command.tags).join(',') : ''} /></label>
            <label>More information URL
                <Input type="url" name="more_info_url" onChange={onFormChange} value={command.more_info_url || ""} /></label>
        </fieldset>

        <fieldset>
            <legend>Automation details</legend>

            <label>Execution environment
                <Select name="executable_type" onChange={onFormChange} value={command.executable_type} required>
                    <option value="custom">Host</option>
                    <option value="rmap">Container</option>
                </Select>
            </label>
            {command.executable_type === 'custom' && <>
                <label>Executable path
                    <Input type="text" name="executable_path" onChange={onFormChange} value={command.executable_path || ""} /></label>
            </>
            }
            {command.executable_type === 'rmap' && <>
                <label>Docker image
                    <Input type="text" name="docker_image" onChange={onFormChange} value={command.docker_image || ""} /></label>
            </>}

            <label>Command line arguments
                <Input type="text" name="arguments" onChange={onFormChange} value={command.arguments || ""} /></label>

            <label>Output capture
                <Select name="output_capture" onChange={onFormChange} value={command.output_capture || "disabled"} required>
                    <option value="disabled">Disabled</option>
                    <option value="stdout">Standard output</option>
                    <option value="path">File path</option>
                </Select>
            </label>

            {command.output_capture === 'path' && <label>Output filename
                <Input type="text" name="output_filename" onChange={onFormChange} value={command.output_filename || ""} />
            </label>}

            {command.output_capture !== 'disabled' && <label>Output parser
                <Select name="output_parser" onChange={onFormChange} value={command.output_parser || ""}>
                    <option value="">(none)</option>
                    {parsers.map(parser => <option key={`parser_${parser.code}`} value={parser.code}>{parser.name}</option>)}
                </Select>
            </label>}
        </fieldset>

        <PrimaryButton type="submit">{isEditForm ? "Save" : "Add"}</PrimaryButton>
    </form>
}
Example #25
Source File: Details.js    From web-client with Apache License 2.0 4 votes vote down vote up
ClientDetails = () => {
    const { clientId } = useParams();
    const navigate = useNavigate();

    const [client] = useFetch(`/clients/${clientId}`);
    const [contacts, fetchContacts] = useFetch(`/clients/${clientId}/contacts`);

    const [contact, setContact] = useState(new Contact());

    const onContactFormChange = ev => {
        setContact({ ...contact, [ev.target.name]: ev.target.value });
    }


    const deleteClient = useDelete(`/clients/`)
    const [logo, setLogo] = useState(null);
    const [smallLogo, setSmallLogo] = useState(null);

    const handleDelete = async () => {
        const confirmed = await deleteClient(clientId);
        if (confirmed)
            navigate('/clients');
    }

    const onFormSubmit = ev => {
        ev.preventDefault();

        secureApiFetch(`/clients/${clientId}/contacts`, { method: 'POST', body: JSON.stringify(contact) })
            .then(resp => {
                if (resp.status === 201) {
                    setContact(new Contact());
                    fetchContacts();
                    actionCompletedToast(`The contact has been added.`);
                } else {
                    errorToast("The contact could not be saved. Review the form data or check the application logs.")
                }
            })
    }

    const onContactDelete = contactId => {
        secureApiFetch(`/contacts/${contactId}`, { method: 'DELETE' })
            .then(() => {
                fetchContacts();
                actionCompletedToast("The contact has been deleted.");
            })
            .catch(err => console.error(err))
    }

    useEffect(() => {
        if (client) {
            if (client.small_logo_attachment_id) {
                downloadAndDisplayLogo(client.small_logo_attachment_id, 'small_logo');
            }

            if (client.logo_attachment_id) {
                downloadAndDisplayLogo(client.logo_attachment_id, 'logo');
            }
        }
    }, [client]);

    const downloadAndDisplayLogo = (logoId, type) => {
        secureApiFetch(`/attachments/${logoId}`, { method: 'GET' })
            .then(resp => {
                const contentDispositionHeader = resp.headers.get('Content-Disposition');
                const filenameRe = new RegExp(/filename="(.*)";/)
                const filename = filenameRe.exec(contentDispositionHeader)[1]
                return Promise.all([resp.blob(), filename]);
            })
            .then((values) => {
                const blob = values[0];
                const url = URL.createObjectURL(blob);
                if (type === 'small_logo') {
                    setSmallLogo(url);
                } else {
                    setLogo(url);
                }
            })
    }

    if (!client) {
        return <Loading />
    }

    return <div>
        <div className='heading'>
            <Breadcrumb>
                <Link to="/clients">Clients</Link>
            </Breadcrumb>
            <ButtonGroup>
                <RestrictedComponent roles={['administrator', 'superuser', 'user']}>
                    <EditButton onClick={(ev) => {
                        ev.preventDefault();
                        navigate(`/clients/${client.id}/edit`)
                    }}>Edit</EditButton>
                    <DeleteButton onClick={handleDelete} />
                </RestrictedComponent>
            </ButtonGroup>
        </div>
        <article>
            <div>
                <PageTitle value={`${client.name} client`} />
                <Title type='Client' title={client.name} icon={<IconBriefcase />} />
            </div>

            <Tabs isLazy>
                <TabList>
                    <Tab>Details</Tab>
                    <Tab>Contacts</Tab>
                    <Tab>Projects</Tab>
                </TabList>
                <TabPanels>
                    <TabPanel>
                        <div className="grid grid-two">
                            <div>
                                <h4>Properties</h4>

                                <dl>
                                    <dt>Address</dt>
                                    <dd>{client.address ?? '-'}</dd>

                                    <dt>URL</dt>
                                    <dd><ExternalLink href={client.url}>{client.url}</ExternalLink></dd>
                                </dl>

                                <h4>Branding</h4>

                                <dl>
                                    <dt>Main logo</dt>
                                    <dd>{logo ? <img src={logo} alt="The main logo" /> : <EmptyField />}</dd>

                                    <dt>Small logo</dt>
                                    <dd>{smallLogo ? <img src={smallLogo} alt="The smaller version of the logo" /> : <EmptyField />}</dd>
                                </dl>
                            </div>

                            <div>
                                <h4>Relations</h4>
                                <dl>
                                    <dt>Created by</dt>
                                    <dd><UserLink userId={client.creator_uid}>{client.creator_full_name}</UserLink></dd>
                                </dl>

                                <TimestampsSection entity={client} />
                            </div>
                        </div>
                    </TabPanel>

                    <TabPanel>
                        <h4>Contacts</h4>

                        {contacts && <>
                            <form onSubmit={onFormSubmit}>
                                <Table>
                                    <Thead>
                                        <Tr>
                                            <Th>Kind</Th>
                                            <Th>Name</Th>
                                            <Th>Role</Th>
                                            <Th>Email</Th>
                                            <Th>Phone</Th>
                                            <Th>&nbsp;</Th>
                                        </Tr>
                                    </Thead>
                                    <Tbody>
                                        <Tr>
                                            <Td>
                                                <Select name="kind" onChange={onContactFormChange} value={contact.kind || ""} isRequired>
                                                    <option value="general">General</option>
                                                    <option value="technical">Technical</option>
                                                    <option value="billing">Billing</option>
                                                </Select>
                                            </Td>
                                            <Td>
                                                <Input type="text" name="name" onChange={onContactFormChange} value={contact.name || ""} isRequired />
                                            </Td>
                                            <Td>
                                                <Input type="text" name="role" onChange={onContactFormChange} value={contact.role || ""} />
                                            </Td>
                                            <Td>
                                                <Input type="email" name="email" onChange={onContactFormChange} value={contact.email || ""} isRequired />
                                            </Td>
                                            <Td>
                                                <Input type="tel" name="phone" onChange={onContactFormChange} value={contact.phone || ""} />
                                            </Td>
                                            <Td>
                                                <Button type="submit">Add</Button>
                                            </Td>
                                        </Tr>
                                        {0 === contacts.length && <NoResultsTableRow numColumns={6} />}
                                        {contacts.map(contact => <>
                                            <Tr key={contact.id}>
                                                <Td>{contact.kind}</Td>
                                                <Td>{contact.name}</Td>
                                                <Td>{contact.role}</Td>
                                                <Td><MailLink email={contact.email} /></Td>
                                                <Td><TelephoneLink number={contact.phone} /></Td>
                                                <Td><DeleteIconButton onClick={onContactDelete.bind(this, contact.id)} /></Td>
                                            </Tr>
                                        </>)}
                                    </Tbody>
                                </Table>
                            </form>
                        </>}
                    </TabPanel>
                    <TabPanel>
                        <ClientProjectsTab clientId={clientId} />
                    </TabPanel>
                </TabPanels>
            </Tabs>

        </article>
    </div>
}
Example #26
Source File: BuilderListView.jsx    From scaffold-directory with MIT License 4 votes vote down vote up
export default function BuilderListView({ serverUrl, mainnetProvider, userRole }) {
  const [builders, setBuilders] = useState([]);
  const [isLoadingBuilders, setIsLoadingBuilders] = useState(false);
  const { secondaryFontColor } = useCustomColorModes();
  const isAdmin = userRole === USER_ROLES.admin;

  const columns = useMemo(
    () => [
      {
        Header: "Builder",
        accessor: "builder",
        disableSortBy: true,
        Cell: ({ value }) => <BuilderAddressCell builderId={value} mainnetProvider={mainnetProvider} />,
      },
      {
        Header: "Challenges",
        accessor: "challenges",
        sortDescFirst: true,
      },
      {
        Header: "Socials",
        accessor: "socials",
        disableSortBy: true,
        Cell: ({ value }) => <BuilderSocialLinksCell builder={value} isAdmin={isAdmin} />,
      },
      {
        Header: "Last Activity",
        accessor: "lastActivity",
        sortDescFirst: true,
        Cell: ({ value }) => <DateWithTooltip timestamp={value} />,
      },
    ],
    // eslint-disable-next-line
    [userRole],
  );

  useEffect(() => {
    async function fetchBuilders() {
      setIsLoadingBuilders(true);
      const fetchedBuilders = await axios.get(serverUrl + serverPath);

      const processedBuilders = fetchedBuilders.data.map(builder => ({
        builder: builder.id,
        challenges: getAcceptedChallenges(builder?.challenges)?.length ?? 0,
        socials: builder,
        lastActivity: builderLastActivity(builder),
      }));

      setBuilders(processedBuilders);
      setIsLoadingBuilders(false);
    }

    fetchBuilders();
  }, [serverUrl]);

  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    prepareRow,
    page,
    canPreviousPage,
    canNextPage,
    pageOptions,
    pageCount,
    gotoPage,
    nextPage,
    previousPage,
    setPageSize,
    state: { pageIndex, pageSize },
  } = useTable(
    {
      columns,
      data: builders,
      initialState: { pageIndex: 0, pageSize: 25, sortBy: useMemo(() => [{ id: "lastActivity", desc: true }], []) },
    },
    useSortBy,
    usePagination,
  );

  return (
    <Container maxW="container.lg">
      <Container maxW="container.md" centerContent>
        <Heading as="h1" mb="4">
          All Builders
        </Heading>
        <Text color={secondaryFontColor} textAlign="center" mb="10">
          List of Ethereum builders creating products, prototypes, and tutorials with{" "}
          <Link href="https://github.com/scaffold-eth/scaffold-eth" color="teal.500" isExternal>
            scaffold-eth
          </Link>
          .
        </Text>
      </Container>
      {isLoadingBuilders ? (
        <BuilderListSkeleton />
      ) : (
        <Box overflowX="auto" mb={8}>
          <Center mb={5}>
            <chakra.strong mr={2}>Total builders:</chakra.strong> {builders.length}
          </Center>
          <Table {...getTableProps()}>
            <Thead>
              {headerGroups.map(headerGroup => (
                <Tr {...headerGroup.getHeaderGroupProps()}>
                  {headerGroup.headers.map(column => (
                    <Th {...column.getHeaderProps(column.getSortByToggleProps())}>
                      {column.render("Header")}
                      <chakra.span pl="4">
                        {column.isSorted ? (
                          column.isSortedDesc ? (
                            <TriangleDownIcon aria-label="sorted descending" />
                          ) : (
                            <TriangleUpIcon aria-label="sorted ascending" />
                          )
                        ) : null}
                      </chakra.span>
                    </Th>
                  ))}
                </Tr>
              ))}
            </Thead>
            <Tbody {...getTableBodyProps()}>
              {page.map(row => {
                prepareRow(row);
                return (
                  <Tr {...row.getRowProps()}>
                    {row.cells.map(cell => (
                      <Td {...cell.getCellProps()}>{cell.render("Cell")}</Td>
                    ))}
                  </Tr>
                );
              })}
            </Tbody>
          </Table>

          <Center mt={4}>
            <ButtonGroup>
              <Button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
                {"<<"}
              </Button>
              <Button onClick={() => previousPage()} disabled={!canPreviousPage}>
                {"<"}
              </Button>
              <Button onClick={() => nextPage()} disabled={!canNextPage}>
                {">"}
              </Button>
              <Button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
                {">>"}
              </Button>
            </ButtonGroup>
          </Center>
          <Center mt={4}>
            <Text mr={4}>
              Page{" "}
              <strong>
                {pageIndex + 1} of {pageOptions.length}
              </strong>{" "}
            </Text>
            <Box>
              <Select
                isFullWidth={false}
                value={pageSize}
                onChange={e => {
                  setPageSize(Number(e.target.value));
                }}
              >
                {[25, 50, 100].map(pageSizeOption => (
                  <option key={pageSizeOption} value={pageSizeOption}>
                    Show {pageSizeOption}
                  </option>
                ))}
              </Select>
            </Box>
          </Center>
        </Box>
      )}
    </Container>
  );
}
Example #27
Source File: playlists.js    From grandcast.fm with Apache License 2.0 4 votes vote down vote up
export default function Playlists() {
  const { isSignedIn } = useAuth()
  const [selectedPlaylist, setSelectedPlaylist] = useState('')
  const [newPlaylist, setNewPlaylist] = useState('')
  const { data } = useQuery(GET_PLAYLISTS)
  const [createPlaylist] = useMutation(CREATE_PLAYLIST)

  const filteredPlaylist = data?.playlists?.filter((p) => {
    return p.name === selectedPlaylist
  })[0]

  return (
    <Container>
      {!isSignedIn() && <SignIn />}
      {isSignedIn() && (
        <div>
          <FormControl id="playlists">
            <Flex>
              <Select
                placeholder="Select playlist"
                onChange={(e) => setSelectedPlaylist(e.target.value)}
              >
                {data?.playlists?.map((p) => {
                  return (
                    <option key={p.name} value={p.value}>
                      {p.name}
                    </option>
                  )
                })}
              </Select>
              <Popover>
                <PopoverTrigger>
                  <Button ml={4}>
                    <AddIcon />
                  </Button>
                </PopoverTrigger>
                <PopoverContent>
                  <PopoverArrow />
                  <PopoverCloseButton />
                  <PopoverHeader>Create new playlist</PopoverHeader>
                  <PopoverBody>
                    <FormControl id="newplaylist">
                      <Input
                        type="text"
                        onChange={(e) => setNewPlaylist(e.target.value)}
                      />
                      <Button
                        mt={4}
                        onClick={() =>
                          createPlaylist({
                            variables: { playlistName: newPlaylist },
                            update: (proxy) => {
                              const data = proxy.readQuery({
                                query: GET_PLAYLISTS,
                              })

                              proxy.writeQuery({
                                query: GET_PLAYLISTS,
                                data: {
                                  playlists: [
                                    ...data.playlists,
                                    {
                                      __typename: 'Playlist',
                                      name: newPlaylist,
                                    },
                                  ],
                                },
                              })
                            },
                          })
                        }
                      >
                        Create
                      </Button>
                    </FormControl>
                  </PopoverBody>
                </PopoverContent>
              </Popover>
            </Flex>
          </FormControl>
          <VStack mt={4} spacing={4}>
            {filteredPlaylist?.episodes?.map((e) => {
              return (
                <Episode key={e.id} episode={e} playlists={data.playlists} />
              )
            })}
          </VStack>
        </div>
      )}
    </Container>
  )
}
Example #28
Source File: ProductAddEdit.js    From react-sample-projects with MIT License 4 votes vote down vote up
ProductAddEdit = () => {
  const categories = useSelector(state => state.product.categories);
  const dispatch = useDispatch();
  const navigate = useNavigate();

  const initialValues = {
    title: '',
    price: '',
    category: '',
    description: '',
    image: '',
  };

  const validationSchema = yup.object({
    title: yup.string().required(),
    price: yup.number().required(),
    category: yup.string().required(),
    description: yup.string().required(),
    image: yup.string().url().required(),
  });

  const onFormSubmit = (values, actions) => {
    actions.setSubmitting(false);
    dispatch(addNewProduct(values));
    navigate('/');
  };

  useEffect(() => {
    dispatch(fetchCategories());
    return () => {};
  }, [dispatch]);

  return (
    <Box boxShadow="base" m={'auto'} width="clamp(300px, 60%, 100%)">
      <Formik
        initialValues={initialValues}
        onSubmit={onFormSubmit}
        validationSchema={validationSchema}
      >
        {props => (
          <Form noValidate>
            <VStack p={3} m="3">
              <Box fontWeight="semibold" mt="1" as="h2" textAlign="left">
                Add Product
              </Box>

              <Field name="title">
                {({ field, form }) => (
                  <FormControl
                    isRequired
                    isInvalid={form.errors.title && form.touched.title}
                  >
                    <FormLabel htmlFor="title">Enter Title</FormLabel>
                    <Input
                      {...field}
                      type="text"
                      id="title"
                      placeholder="Enter Title"
                    />
                    <ErrorMessage
                      name="title"
                      component={FormErrorMessage}
                    ></ErrorMessage>
                  </FormControl>
                )}
              </Field>

              <Field name="price">
                {({ field, form }) => (
                  <FormControl
                    isRequired
                    isInvalid={form.errors.price && form.touched.price}
                  >
                    <FormLabel>Enter price</FormLabel>
                    <Input type="number" placeholder="Enter price" {...field} />
                    <ErrorMessage
                      name="price"
                      component={FormErrorMessage}
                    ></ErrorMessage>
                  </FormControl>
                )}
              </Field>
              <Field name="category">
                {({ field, form }) => (
                  <FormControl
                    name="category"
                    isRequired
                    isInvalid={form.errors.category && form.touched.category}
                  >
                    <FormLabel>Enter category</FormLabel>
                    <Select placeholder="Select category" {...field}>
                      {categories.map((category, index) => (
                        <option key={index}>{category}</option>
                      ))}
                    </Select>
                    <ErrorMessage
                      name="category"
                      component={FormErrorMessage}
                    ></ErrorMessage>
                  </FormControl>
                )}
              </Field>
              <Field name="description">
                {({ field, form }) => (
                  <FormControl
                    name="description"
                    isRequired
                    isInvalid={
                      form.errors.description && form.touched.description
                    }
                  >
                    <FormLabel>Enter description</FormLabel>
                    <Textarea
                      {...field}
                      id="description"
                      placeholder="Enter description"
                    ></Textarea>
                    <ErrorMessage
                      name="description"
                      component={FormErrorMessage}
                    ></ErrorMessage>
                  </FormControl>
                )}
              </Field>
              <Field name="image">
                {({ field, form }) => (
                  <FormControl
                    name="image"
                    isRequired
                    isInvalid={form.errors.image && form.touched.image}
                  >
                    <FormLabel>Enter image</FormLabel>
                    <Input
                      type="url"
                      placeholder="Enter image url"
                      {...field}
                    />

                    <ErrorMessage
                      name="image"
                      component={FormErrorMessage}
                    ></ErrorMessage>
                  </FormControl>
                )}
              </Field>
              <Button
                mt={4}
                colorScheme="teal"
                type="submit"
                isLoading={props.isSubmitting}
              >
                Add Product
              </Button>
            </VStack>
          </Form>
        )}
      </Formik>
    </Box>
  );
}