diff --git a/frontend/src/pages/Organization/OrgScanHistory.tsx b/frontend/src/pages/Organization/OrgScanHistory.tsx new file mode 100644 index 00000000..207e9e21 --- /dev/null +++ b/frontend/src/pages/Organization/OrgScanHistory.tsx @@ -0,0 +1,200 @@ +import React, { useState, useCallback } from 'react'; +import { useAuthContext } from 'context'; +import { + Organization as OrganizationType, + ScanTask, + Scan, + ScanSchema +} from 'types'; +import { Button } from '@mui/material'; +import { Column } from 'react-table'; +import { Table } from 'components'; +// @ts-ignore:next-line +import { formatDistanceToNow, parseISO } from 'date-fns'; + +interface OrganizationScan extends Partial { + id: any; + granularScans: any; +} + +type OrgScanHistoryProps = { + organization: OrganizationScan; + setOrganization: Function; + scanTasks: ScanTask[]; +}; + +export const OrgScanHistory: React.FC = ({ + organization, + setOrganization, + scanTasks +}) => { + const { apiGet, apiPost, user, setFeedbackMessage } = useAuthContext(); + const [scans, setScans] = useState([]); + const [scanSchema, setScanSchema] = useState({}); + + const dateAccessor = (date?: string) => { + return !date || new Date(date).getTime() === new Date(0).getTime() + ? 'None' + : `${formatDistanceToNow(parseISO(date))} ago`; + }; + + const fetchScans = useCallback(async () => { + try { + const response = await apiGet<{ + scans: Scan[]; + schema: ScanSchema; + }>('/granularScans/'); + let { scans } = response; + const { schema } = response; + + if (user?.userType !== 'globalAdmin') + scans = scans.filter( + (scan) => + scan.name !== 'censysIpv4' && scan.name !== 'censysCertificates' + ); + + setScans(scans); + setScanSchema(schema); + } catch (e) { + console.error(e); + } + }, [apiGet, user]); + + const updateScan = async (scan: Scan, enabled: boolean) => { + try { + if (!organization) return; + await apiPost( + `/organizations/${organization?.id}/granularScans/${scan.id}/update`, + { + body: { + enabled + } + } + ); + setOrganization({ + ...organization, + granularScans: enabled + ? organization.granularScans.concat([scan]) + : organization.granularScans.filter( + (granularScan: { id: string }) => granularScan.id !== scan.id + ) + }); + } catch (e: any) { + setFeedbackMessage({ + message: + e.status === 422 ? 'Error updating scan' : e.message ?? e.toString(), + type: 'error' + }); + console.error(e); + } + }; + const scanColumns: Column[] = [ + { + Header: 'Name', + accessor: 'name', + width: 150, + id: 'name', + disableFilters: true + }, + { + Header: 'Description', + accessor: ({ name }) => scanSchema[name] && scanSchema[name].description, + width: 200, + minWidth: 200, + id: 'description', + disableFilters: true + }, + { + Header: 'Mode', + accessor: ({ name }) => + scanSchema[name] && scanSchema[name].isPassive ? 'Passive' : 'Active', + width: 150, + minWidth: 150, + id: 'mode', + disableFilters: true + }, + { + Header: 'Action', + id: 'action', + maxWidth: 100, + Cell: ({ row }: { row: { index: number } }) => { + if (!organization) return null; + const enabled = organization.granularScans.find( + (scan: { id: string }) => scan.id === scans[row.index].id + ); + return ( + + ); + }, + disableFilters: true + } + ]; + + const scanTaskColumns: Column[] = [ + { + Header: 'ID', + accessor: 'id', + disableFilters: true + }, + { + Header: 'Status', + accessor: 'status', + disableFilters: true + }, + { + Header: 'Type', + accessor: 'type', + disableFilters: true + }, + { + Header: 'Name', + accessor: ({ scan }) => scan?.name, + disableFilters: true + }, + { + Header: 'Created At', + accessor: ({ createdAt }) => dateAccessor(createdAt), + disableFilters: true, + disableSortBy: true + }, + { + Header: 'Requested At', + accessor: ({ requestedAt }) => dateAccessor(requestedAt), + disableFilters: true, + disableSortBy: true + }, + { + Header: 'Started At', + accessor: ({ startedAt }) => dateAccessor(startedAt), + disableFilters: true, + disableSortBy: true + }, + { + Header: 'Finished At', + accessor: ({ finishedAt }) => dateAccessor(finishedAt), + disableFilters: true, + disableSortBy: true + }, + { + Header: 'Output', + accessor: 'output', + disableFilters: true + } + ]; + return ( + <> + columns={scanColumns} data={scans} fetchData={fetchScans} /> +

Organization Scan History

+ columns={scanTaskColumns} data={scanTasks} /> + + ); +}; + +export default OrgScanHistory; diff --git a/frontend/src/pages/Organization/OrgSettings.tsx b/frontend/src/pages/Organization/OrgSettings.tsx new file mode 100644 index 00000000..26473b8f --- /dev/null +++ b/frontend/src/pages/Organization/OrgSettings.tsx @@ -0,0 +1,488 @@ +import React from 'react'; +import { useAuthContext } from 'context'; +import { + PendingDomain, + Organization as OrganizationType, + OrganizationTag +} from 'types'; +import { + Alert, + Autocomplete, + Button, + Chip, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + Grid, + Stack, + Switch, + TextField, + Typography +} from '@mui/material'; +import { CheckCircleOutline, Place, Public } from '@mui/icons-material'; +import InfoDialog from 'components/Dialog/InfoDialog'; + +interface AutocompleteType extends Partial { + title?: string; +} + +interface OrgSettingsType extends Partial { + id: any; + granularScans: any; + rootDomains: string[]; + ipBlocks: string[]; + tags: OrganizationTag[]; + pendingDomains: PendingDomain[]; +} + +type OrgSettingsProps = { + organization: OrgSettingsType; + setOrganization: Function; + tags: AutocompleteType[]; +}; + +export const OrgSettings: React.FC = ({ + organization, + setOrganization, + tags +}) => { + const { apiPut, apiPost, user, setFeedbackMessage } = useAuthContext(); + const [inputValue, setInputValue] = React.useState(''); + const [dialog, setDialog] = React.useState<{ + open: boolean; + type?: 'rootDomains' | 'ipBlocks' | 'tags'; + label?: string; + stage?: number; + domainVerificationStatusMessage?: string; + }>({ open: false }); + const [isSaveDisabled, setIsSaveDisabled] = React.useState(true); + const [infoDialogOpen, setInfoDialogOpen] = React.useState(false); + const [chosenTags, setChosenTags] = React.useState( + organization.tags ? organization.tags.map((tag) => tag.name) : [] + ); + + const updateOrganization = async (body: any) => { + try { + const org = await apiPut('/organizations/' + organization?.id, { + body: organization + }); + setOrganization(org); + setFeedbackMessage({ + message: 'Organization successfully updated', + type: 'success' + }); + setInfoDialogOpen(true); + } catch (e: any) { + setFeedbackMessage({ + message: + e.status === 422 + ? 'Error updating organization' + : e.message ?? e.toString(), + type: 'error' + }); + console.error(e); + } + }; + + const initiateDomainVerification = async (domain: string) => { + try { + if (!organization) return; + const pendingDomains: PendingDomain[] = await apiPost( + `/organizations/${organization?.id}/initiateDomainVerification`, + { + body: { domain } + } + ); + setOrganization({ ...organization, pendingDomains }); + } catch (e: any) { + setFeedbackMessage({ + message: + e.status === 422 + ? 'Error creating domain' + : e.message ?? e.toString(), + type: 'error' + }); + console.error(e); + } + }; + + const checkDomainVerification = async (domain: string) => { + try { + if (!organization) return; + const resp: { success: boolean; organization?: OrganizationType } = + await apiPost( + `/organizations/${organization?.id}/checkDomainVerification`, + { + body: { domain } + } + ); + if (resp.success && resp.organization) { + setOrganization(resp.organization); + setDialog({ open: false }); + setFeedbackMessage({ + message: 'Domain ' + inputValue + ' successfully verified!', + type: 'success' + }); + } else { + setDialog({ + ...dialog, + domainVerificationStatusMessage: + 'Record not yet found. Note that DNS records may take up to 72 hours to propagate. You can come back later to check the verification status.' + }); + } + } catch (e: any) { + setFeedbackMessage({ + message: + e.status === 422 + ? 'Error verifying domain' + : e.message ?? e.toString(), + type: 'error' + }); + console.error(e); + } + }; + + const handleTagChange = (event: any, newValue: any) => { + setChosenTags(newValue); + setOrganization((prevValues: any) => ({ + ...prevValues, + tags: newValue.map((tag: any) => ({ name: tag })) + })); + setIsSaveDisabled(false); + }; + + const ListInput = (props: { + type: 'rootDomains' | 'ipBlocks' | 'tags'; + label: string; + }) => { + if (!organization) return null; + const elements: (string | OrganizationTag)[] = organization[props.type]; + return ( + + + {props.label} + + {elements && + elements.map((value: string | OrganizationTag, index: number) => ( + + { + organization[props.type].splice(index, 1); + setOrganization({ ...organization }); + if (chosenTags.length > 0) { + chosenTags.splice(index, 1); + setChosenTags(chosenTags); + } + setIsSaveDisabled(false); + }} + > + + ))} + {props.type === 'rootDomains' && + organization.pendingDomains.map((domain, index: number) => ( + + { + organization.pendingDomains.splice(index, 1); + setOrganization({ ...organization }); + }} + onClick={() => { + setInputValue(domain.name); + setDialog({ + open: true, + type: props.type, + label: props.label, + stage: 1 + }); + }} + > + + ))} + {(props.type === 'rootDomains' || user?.userType === 'globalAdmin') && ( + + { + setDialog({ + open: true, + type: props.type, + label: props.label, + stage: 0 + }); + }} + /> + + )} + + ); + }; + + if (!organization) return null; + return ( + <> + setDialog({ open: false })} + aria-labelledby="form-dialog-title" + maxWidth="xs" + fullWidth + > + + {dialog.type === 'tags' ? 'Update ' : 'Add '} + {dialog.label && dialog.label.slice(0, -1)}(s) + + + {dialog.type === 'tags' ? ( + <> + + Use the dropdown to select or deselect existing tags. +
+ -- OR -- +
+ Type and then press enter to add a new tag. +
+ + value.map((option: string, index: number) => { + const { key, ...tagProps } = getTagProps({ index }); + return ( + + ); + }) + } + sx={{ mt: 1 }} + multiple + options={tags + .map((option) => option.name) + .filter((name): name is string => name !== undefined)} + freeSolo + renderInput={(params) => ( + + )} + /> + + ) : dialog.type === 'rootDomains' && dialog.stage === 1 ? ( + <> + + Add the following TXT record to {inputValue}'s DNS + configuration and click Verify. + + domain.name === inputValue + )?.token + } + onFocus={(event) => { + event.target.select(); + }} + /> + {dialog.domainVerificationStatusMessage && ( + <> +

+

+ + {dialog.domainVerificationStatusMessage} + + + )} + + ) : user?.userType === 'globalAdmin' ? ( + <> + + Separate multiple entries by commas. + + setInputValue(e.target.value)} + /> + + ) : dialog.type === 'rootDomains' && dialog.stage === 0 ? ( + <> + + In order to add a root domain, you will need to verify ownership + of the domain. + + setInputValue(e.target.value)} + /> + + ) : ( + <> + )} +
+ + + + +
+ { + setInfoDialogOpen(false); + setIsSaveDisabled(true); + }} + icon={} + title={Success } + content={ + + {organization.name} was successfully updated. + + } + /> + + + + + + + {organization.regionId && ( + <> + + + Region {organization.regionId} + + + )} + {(organization.stateName || organization.state) && ( + <> + + + {organization.stateName || organization.state} + + + )} + + + + + + + + + {user?.userType === 'globalAdmin' && ( + + + + )} + + + + Passive Mode + + + ) => { + setOrganization({ + ...organization, + isPassive: event.target.checked + }); + if (!organization.isPassive) { + setIsSaveDisabled(false); + } + }} + color="primary" + /> + + + + {organization.rootDomains.length === 0 && ( + + + An organization must have at least one Root Domain. + + + )} + + + + + + + ); +}; + +export default OrgSettings; diff --git a/frontend/src/pages/Organization/Organization.tsx b/frontend/src/pages/Organization/Organization.tsx index f044a992..3e9c58ea 100644 --- a/frontend/src/pages/Organization/Organization.tsx +++ b/frontend/src/pages/Organization/Organization.tsx @@ -1,180 +1,42 @@ import React, { useEffect, useState, useCallback } from 'react'; -import * as OrganizationStyles from './style'; -import { Link, useParams } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; import { useAuthContext } from 'context'; import { Organization as OrganizationType, Role, ScanTask, - Scan, - ScanSchema, - OrganizationTag, - PendingDomain + OrganizationTag } from 'types'; -import { Column } from 'react-table'; -import { Table } from 'components'; -// @ts-ignore:next-line -import { formatDistanceToNow, parseISO } from 'date-fns'; + import { Box, Breadcrumbs, - Chip, - Button, - Dialog, - DialogActions, - DialogContent, - DialogContentText, - DialogTitle, Grid, Link as MuiLink, Paper, - Switch as SwitchInput, Tab, - TextField, Typography } from '@mui/material'; import { TabContext, TabList, TabPanel } from '@mui/lab'; import { ChevronRight } from '@mui/icons-material'; -import { Autocomplete } from '@mui/material'; -import { createFilterOptions } from '@mui/material/useAutocomplete'; -import { OrganizationList } from 'components/OrganizationList'; import OrgMembers from './OrgMembers'; +import OrgScanHistory from './OrgScanHistory'; +import OrgSettings from './OrgSettings'; interface AutocompleteType extends Partial { title?: string; } - export const Organization: React.FC = () => { - const { apiGet, apiPut, apiPost, user, setFeedbackMessage } = - useAuthContext(); + const { apiGet } = useAuthContext(); const { organizationId } = useParams<{ organizationId: string }>(); const [organization, setOrganization] = useState(); const [tags, setTags] = useState([]); const [userRoles, setUserRoles] = useState([]); const [scanTasks, setScanTasks] = useState([]); - const [scans, setScans] = useState([]); - const [scanSchema, setScanSchema] = useState({}); const [tabValue, setTabValue] = React.useState('1'); - const [tagValue, setTagValue] = React.useState(null); - const [inputValue, setInputValue] = React.useState(''); - const [dialog, setDialog] = React.useState<{ - open: boolean; - type?: 'rootDomains' | 'ipBlocks' | 'tags'; - label?: string; - stage?: number; - domainVerificationStatusMessage?: string; - }>({ open: false }); const handleTabChange = (event: React.SyntheticEvent, newValue: string) => { setTabValue(newValue); }; - const dateAccessor = (date?: string) => { - return !date || new Date(date).getTime() === new Date(0).getTime() - ? 'None' - : `${formatDistanceToNow(parseISO(date))} ago`; - }; - - const organizationClasses = OrganizationStyles.organizationClasses; - - const scanColumns: Column[] = [ - { - Header: 'Name', - accessor: 'name', - width: 150, - id: 'name', - disableFilters: true - }, - { - Header: 'Description', - accessor: ({ name }) => scanSchema[name] && scanSchema[name].description, - width: 200, - minWidth: 200, - id: 'description', - disableFilters: true - }, - { - Header: 'Mode', - accessor: ({ name }) => - scanSchema[name] && scanSchema[name].isPassive ? 'Passive' : 'Active', - width: 150, - minWidth: 150, - id: 'mode', - disableFilters: true - }, - { - Header: 'Action', - id: 'action', - maxWidth: 100, - Cell: ({ row }: { row: { index: number } }) => { - if (!organization) return null; - const enabled = organization.granularScans.find( - (scan) => scan.id === scans[row.index].id - ); - return ( - - ); - }, - disableFilters: true - } - ]; - - const scanTaskColumns: Column[] = [ - { - Header: 'ID', - accessor: 'id', - disableFilters: true - }, - { - Header: 'Status', - accessor: 'status', - disableFilters: true - }, - { - Header: 'Type', - accessor: 'type', - disableFilters: true - }, - { - Header: 'Name', - accessor: ({ scan }) => scan?.name, - disableFilters: true - }, - { - Header: 'Created At', - accessor: ({ createdAt }) => dateAccessor(createdAt), - disableFilters: true, - disableSortBy: true - }, - { - Header: 'Requested At', - accessor: ({ requestedAt }) => dateAccessor(requestedAt), - disableFilters: true, - disableSortBy: true - }, - { - Header: 'Started At', - accessor: ({ startedAt }) => dateAccessor(startedAt), - disableFilters: true, - disableSortBy: true - }, - { - Header: 'Finished At', - accessor: ({ finishedAt }) => dateAccessor(finishedAt), - disableFilters: true, - disableSortBy: true - }, - { - Header: 'Output', - accessor: 'output', - disableFilters: true - } - ]; const fetchOrganization = useCallback(async () => { try { @@ -195,453 +57,19 @@ export const Organization: React.FC = () => { } }, [apiGet, setOrganization, organizationId]); - const fetchScans = useCallback(async () => { - try { - const response = await apiGet<{ - scans: Scan[]; - schema: ScanSchema; - }>('/granularScans/'); - let { scans } = response; - const { schema } = response; - - if (user?.userType !== 'globalAdmin') - scans = scans.filter( - (scan) => - scan.name !== 'censysIpv4' && scan.name !== 'censysCertificates' - ); - - setScans(scans); - setScanSchema(schema); - } catch (e) { - console.error(e); - } - }, [apiGet, user]); - - const updateOrganization = async (body: any) => { - try { - const org = await apiPut('/organizations/' + organization?.id, { - body: organization - }); - setOrganization(org); - setFeedbackMessage({ - message: 'Organization successfully updated', - type: 'success' - }); - } catch (e: any) { - setFeedbackMessage({ - message: - e.status === 422 - ? 'Error updating organization' - : e.message ?? e.toString(), - type: 'error' - }); - console.error(e); - } - }; - - const updateScan = async (scan: Scan, enabled: boolean) => { - try { - if (!organization) return; - await apiPost( - `/organizations/${organization?.id}/granularScans/${scan.id}/update`, - { - body: { - enabled - } - } - ); - setOrganization({ - ...organization, - granularScans: enabled - ? organization.granularScans.concat([scan]) - : organization.granularScans.filter( - (granularScan) => granularScan.id !== scan.id - ) - }); - } catch (e: any) { - setFeedbackMessage({ - message: - e.status === 422 ? 'Error updating scan' : e.message ?? e.toString(), - type: 'error' - }); - console.error(e); - } - }; - - const initiateDomainVerification = async (domain: string) => { - try { - if (!organization) return; - const pendingDomains: PendingDomain[] = await apiPost( - `/organizations/${organization?.id}/initiateDomainVerification`, - { - body: { domain } - } - ); - setOrganization({ ...organization, pendingDomains }); - } catch (e: any) { - setFeedbackMessage({ - message: - e.status === 422 - ? 'Error creating domain' - : e.message ?? e.toString(), - type: 'error' - }); - console.error(e); - } - }; - - const checkDomainVerification = async (domain: string) => { - try { - if (!organization) return; - const resp: { success: boolean; organization?: OrganizationType } = - await apiPost( - `/organizations/${organization?.id}/checkDomainVerification`, - { - body: { domain } - } - ); - if (resp.success && resp.organization) { - setOrganization(resp.organization); - setDialog({ open: false }); - setFeedbackMessage({ - message: 'Domain ' + inputValue + ' successfully verified!', - type: 'success' - }); - } else { - setDialog({ - ...dialog, - domainVerificationStatusMessage: - 'Record not yet found. Note that DNS records may take up to 72 hours to propagate. You can come back later to check the verification status.' - }); - } - } catch (e: any) { - setFeedbackMessage({ - message: - e.status === 422 - ? 'Error verifying domain' - : e.message ?? e.toString(), - type: 'error' - }); - console.error(e); - } - }; - useEffect(() => { fetchOrganization(); }, [fetchOrganization]); - const filter = createFilterOptions(); - - const ListInput = (props: { - type: 'rootDomains' | 'ipBlocks' | 'tags'; - label: string; - }) => { - if (!organization) return null; - const elements: (string | OrganizationTag)[] = organization[props.type]; - return ( - - - {props.label} - - {elements && - elements.map((value: string | OrganizationTag, index: number) => ( - - { - organization[props.type].splice(index, 1); - setOrganization({ ...organization }); - }} - > - - ))} - {props.type === 'rootDomains' && - organization.pendingDomains.map((domain, index: number) => ( - - { - organization.pendingDomains.splice(index, 1); - setOrganization({ ...organization }); - }} - onClick={() => { - setInputValue(domain.name); - setDialog({ - open: true, - type: props.type, - label: props.label, - stage: 1 - }); - }} - > - - ))} - {(props.type === 'rootDomains' || user?.userType === 'globalAdmin') && ( - - { - setDialog({ - open: true, - type: props.type, - label: props.label, - stage: 0 - }); - }} - /> - - )} - - ); - }; - if (!organization) return null; const views = [ - setDialog({ open: false })} - aria-labelledby="form-dialog-title" - maxWidth="xs" - fullWidth - > - - Add {dialog.label && dialog.label.slice(0, -1)} - - - {dialog.type === 'tags' ? ( - <> - - Select an existing tag or add a new one. - - { - if (typeof newValue === 'string') { - setTagValue({ - name: newValue - }); - } else { - setTagValue(newValue); - } - }} - filterOptions={(options, params) => { - const filtered = filter(options, params); - // Suggest the creation of a new value - if ( - params.inputValue !== '' && - !filtered.find( - (tag) => - tag.name?.toLowerCase() === - params.inputValue.toLowerCase() - ) - ) { - filtered.push({ - name: params.inputValue, - title: `Add "${params.inputValue}"` - }); - } - return filtered; - }} - selectOnFocus - clearOnBlur - handleHomeEndKeys - options={tags} - getOptionLabel={(option) => { - if (typeof option === 'string') { - return option; - } - return (option as AutocompleteType).name ?? ''; - }} - renderOption={(props, option) => { - if (option.title) return option.title; - return option.name ?? ''; - }} - fullWidth - freeSolo - renderInput={(params) => ( - - )} - /> - - ) : dialog.type === 'rootDomains' && dialog.stage === 1 ? ( - <> - - Add the following TXT record to {inputValue}'s DNS - configuration and click Verify. - - domain.name === inputValue - )?.token - } - onFocus={(event) => { - event.target.select(); - }} - /> - {dialog.domainVerificationStatusMessage && ( - <> -

-

- - {dialog.domainVerificationStatusMessage} - - - )} - - ) : user?.userType === 'globalAdmin' ? ( - <> - - Separate multiple entries by commas. - - setInputValue(e.target.value)} - /> - - ) : dialog.type === 'rootDomains' && dialog.stage === 0 ? ( - <> - - In order to add a root domain, you will need to verify ownership - of the domain. - - setInputValue(e.target.value)} - /> - - ) : ( - <> - )} -
- - - - -
- - - - - - - - - - - {user?.userType === 'globalAdmin' && ( - - - - )} - - - - Passive Mode - - - ) => { - setOrganization({ - ...organization, - isPassive: event.target.checked - }); - }} - color="primary" - /> - - - - - - - - - - +
, { /> , - - , - - columns={scanColumns} data={scans} fetchData={fetchScans} /> -

Organization Scan History

- columns={scanTaskColumns} data={scanTasks} /> +
]; diff --git a/frontend/src/pages/Organization/style.ts b/frontend/src/pages/Organization/style.ts deleted file mode 100644 index f30b2c22..00000000 --- a/frontend/src/pages/Organization/style.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { styled } from '@mui/material/styles'; - -const PREFIX = 'Organization'; - -export const organizationClasses = { - header: `${PREFIX}-header`, - headerLabel: `${PREFIX}-headerLabel`, - chip: `${PREFIX}-chip`, - settingsWrapper: `${PREFIX}-settingsWrapper`, - buttons: `${PREFIX}-buttons`, - orgName: `${PREFIX}-orgName`, - textField: `${PREFIX}-textField`, - root: `${PREFIX}-root`, - headerRow: `${PREFIX}-headerRow` -}; - -export const OrganizationRoot = styled('div')(({ theme }) => ({ - [`& .${organizationClasses.header}`]: { - background: '#F9F9F9' - }, - - [`& .${organizationClasses.headerLabel}`]: { - margin: 0, - paddingTop: '1.5rem', - paddingBottom: '0.5rem', - marginLeft: '15%', - color: '#C9C9C9', - fontWeight: 500, - fontStyle: 'normal', - fontSize: '24px', - '& a': { - textDecoration: 'none', - color: '#C9C9C9' - }, - '& svg': { - verticalAlign: 'middle', - lineHeight: '100%', - fontSize: '26px' - } - }, - - [`& .${organizationClasses.chip}`]: { - color: 'white', - marginRight: '10px', - marginTop: '10px' - }, - - [`& .${organizationClasses.settingsWrapper}`]: { - boxSizing: 'border-box', - border: '0px', - boxShadow: 'none', - borderRadius: '0px', - padding: '25px', - maxWidth: '900px', - margin: '0 auto' - }, - - [`& .${organizationClasses.buttons}`]: { - display: 'flex', - justifyContent: 'flex-end' - }, - - [`& .${organizationClasses.orgName}`]: { - background: '#F5F5F5 !important', - paddingBottom: '10px' - }, - - [`& .${organizationClasses.textField}`]: { - background: '#F5F5F5 !important' - }, - - [`& .${organizationClasses.root}`]: { - maxWidth: '1400px', - margin: '0 auto', - '@media screen and (min-width: 480px)': { - padding: '1rem 1rem' - }, - '@media screen and (min-width: 640px)': { - padding: '1rem 1.5rem' - }, - '@media screen and (min-width: 1024px)': { - padding: '1rem 2rem' - } - }, - - [`& .${organizationClasses.headerRow}`]: { - padding: '0.5rem 0', - width: '100%', - display: 'flex', - alignItems: 'center', - fontSize: '16px', - flexWrap: 'wrap', - '& label': { - flex: '1 0 100%', - fontWeight: 'bolder', - display: 'flex', - alignItems: 'center', - padding: '0.5rem 0', - '@media screen and (min-width: 640px)': { - flex: '0 0 220px', - padding: 0 - } - }, - '& span': { - display: 'block', - flex: '1 1 auto', - marginLeft: 'calc(1rem + 20px)', - '@media screen and (min-width: 640px)': { - marginLeft: 'calc(1rem + 20px)' - }, - '@media screen and (min-width: 1024px)': { - marginLeft: 0 - } - } - } -}));