placeholder?: boolean; | placeholder?: boolean; | ||||
} | } | ||||
/** @deprecated Use Spinner from Echoes instead. | |||||
* | |||||
* Some of the props have changed or been renamed: | |||||
* - ~`customSpinner`~ has been removed | |||||
* - `loading` is now `isLoading` | |||||
* - `placeholder` is now `hasPlaceholder` | |||||
*/ | |||||
export function Spinner(props: React.PropsWithChildren<Props>) { | export function Spinner(props: React.PropsWithChildren<Props>) { | ||||
const intl = useIntl(); | const intl = useIntl(); | ||||
const { | const { |
* along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import styled from '@emotion/styled'; | import styled from '@emotion/styled'; | ||||
import { Link, Spinner } from '@sonarsource/echoes-react'; | |||||
import { formatDistance } from 'date-fns'; | import { formatDistance } from 'date-fns'; | ||||
import { CheckIcon, FlagMessage, FlagWarningIcon, Link, Spinner, themeColor } from 'design-system'; | |||||
import { CheckIcon, FlagMessage, FlagWarningIcon, themeColor } from 'design-system'; | |||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { FormattedMessage } from 'react-intl'; | import { FormattedMessage } from 'react-intl'; | ||||
import { translate, translateWithParameters } from '../../helpers/l10n'; | import { translate, translateWithParameters } from '../../helpers/l10n'; | ||||
import { TaskStatuses } from '../../types/tasks'; | import { TaskStatuses } from '../../types/tasks'; | ||||
interface SynchronisationWarningProps { | interface SynchronisationWarningProps { | ||||
short?: boolean; | |||||
data: AlmSyncStatus; | data: AlmSyncStatus; | ||||
short?: boolean; | |||||
} | } | ||||
interface LastSyncProps { | interface LastSyncProps { | ||||
short?: boolean; | |||||
info: AlmSyncStatus['lastSync']; | info: AlmSyncStatus['lastSync']; | ||||
short?: boolean; | |||||
} | } | ||||
function LastSyncAlert({ info, short }: Readonly<LastSyncProps>) { | function LastSyncAlert({ info, short }: Readonly<LastSyncProps>) { | ||||
if (info === undefined) { | if (info === undefined) { | ||||
return null; | return null; | ||||
} | } | ||||
const { finishedAt, errorMessage, status, summary, warningMessage } = info; | const { finishedAt, errorMessage, status, summary, warningMessage } = info; | ||||
const formattedDate = finishedAt ? formatDistance(new Date(finishedAt), new Date()) : ''; | const formattedDate = finishedAt ? formatDistance(new Date(finishedAt), new Date()) : ''; | ||||
<CheckIcon width={32} height={32} className="sw-mr-2" /> | <CheckIcon width={32} height={32} className="sw-mr-2" /> | ||||
)} | )} | ||||
</IconWrapper> | </IconWrapper> | ||||
<i> | <i> | ||||
{warningMessage ? ( | {warningMessage ? ( | ||||
<FormattedMessage | <FormattedMessage | ||||
id="settings.authentication.github.synchronization_successful.with_warning" | |||||
defaultMessage={translate( | defaultMessage={translate( | ||||
'settings.authentication.github.synchronization_successful.with_warning', | 'settings.authentication.github.synchronization_successful.with_warning', | ||||
)} | )} | ||||
id="settings.authentication.github.synchronization_successful.with_warning" | |||||
values={{ | values={{ | ||||
date: formattedDate, | date: formattedDate, | ||||
details: ( | details: ( | ||||
<FlagMessage variant="error"> | <FlagMessage variant="error"> | ||||
<div> | <div> | ||||
<FormattedMessage | <FormattedMessage | ||||
id="settings.authentication.github.synchronization_failed_short" | |||||
defaultMessage={translate( | defaultMessage={translate( | ||||
'settings.authentication.github.synchronization_failed_short', | 'settings.authentication.github.synchronization_failed_short', | ||||
)} | )} | ||||
id="settings.authentication.github.synchronization_failed_short" | |||||
values={{ | values={{ | ||||
details: ( | details: ( | ||||
<Link className="sw-ml-2" to="/admin/settings?category=authentication&tab=github"> | <Link className="sw-ml-2" to="/admin/settings?category=authentication&tab=github"> | ||||
return ( | return ( | ||||
<> | <> | ||||
<FlagMessage | <FlagMessage | ||||
variant={status === TaskStatuses.Success ? 'success' : 'error'} | |||||
role="alert" | |||||
aria-live="assertive" | aria-live="assertive" | ||||
role="alert" | |||||
variant={status === TaskStatuses.Success ? 'success' : 'error'} | |||||
> | > | ||||
<div> | <div> | ||||
{status === TaskStatuses.Success ? ( | {status === TaskStatuses.Success ? ( | ||||
'settings.authentication.github.synchronization_successful', | 'settings.authentication.github.synchronization_successful', | ||||
formattedDate, | formattedDate, | ||||
)} | )} | ||||
<br /> | <br /> | ||||
{summary ?? ''} | {summary ?? ''} | ||||
</> | </> | ||||
) : ( | ) : ( | ||||
formattedDate, | formattedDate, | ||||
)} | )} | ||||
</div> | </div> | ||||
<br /> | <br /> | ||||
{errorMessage ?? ''} | {errorMessage ?? ''} | ||||
</React.Fragment> | </React.Fragment> | ||||
)} | )} | ||||
</div> | </div> | ||||
</FlagMessage> | </FlagMessage> | ||||
<FlagMessage variant="warning" role="alert" aria-live="assertive"> | <FlagMessage variant="warning" role="alert" aria-live="assertive"> | ||||
{warningMessage} | {warningMessage} | ||||
</FlagMessage> | </FlagMessage> | ||||
} | } | ||||
export default function AlmSynchronisationWarning({ | export default function AlmSynchronisationWarning({ | ||||
short, | |||||
data, | data, | ||||
short, | |||||
}: Readonly<SynchronisationWarningProps>) { | }: Readonly<SynchronisationWarningProps>) { | ||||
const loadingLabel = | const loadingLabel = | ||||
data.nextSync && | data.nextSync && | ||||
? 'settings.authentication.github.synchronization_pending' | ? 'settings.authentication.github.synchronization_pending' | ||||
: 'settings.authentication.github.synchronization_in_progress', | : 'settings.authentication.github.synchronization_in_progress', | ||||
); | ); | ||||
return ( | return ( | ||||
<> | <> | ||||
{!short && ( | {!short && ( | ||||
<div className={data.nextSync ? 'sw-flex sw-gap-2 sw-mb-4' : ''}> | <div className={data.nextSync ? 'sw-flex sw-gap-2 sw-mb-4' : ''}> | ||||
<Spinner loading={!!data.nextSync} ariaLabel={loadingLabel} /> | |||||
<Spinner ariaLabel={loadingLabel} isLoading={!!data.nextSync} /> | |||||
<div>{data.nextSync && loadingLabel}</div> | <div>{data.nextSync && loadingLabel}</div> | ||||
</div> | </div> | ||||
)} | )} |
* along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import { Spinner } from '@sonarsource/echoes-react'; | |||||
import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
import { ItemButton, Spinner } from 'design-system'; | |||||
import { ItemButton } from 'design-system'; | |||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { translate } from '../../../helpers/l10n'; | import { translate } from '../../../helpers/l10n'; | ||||
event.preventDefault(); | event.preventDefault(); | ||||
event.stopPropagation(); | event.stopPropagation(); | ||||
event.currentTarget.blur(); | event.currentTarget.blur(); | ||||
if (qualifier) { | |||||
if (qualifier !== '') { | |||||
this.props.onMoreClick(qualifier); | this.props.onMoreClick(qualifier); | ||||
} | } | ||||
}; | }; | ||||
handleMouseEnter = (qualifier: string) => { | handleMouseEnter = (qualifier: string) => { | ||||
if (qualifier) { | |||||
if (qualifier !== '') { | |||||
this.props.onSelect(`qualifier###${qualifier}`); | this.props.onSelect(`qualifier###${qualifier}`); | ||||
} | } | ||||
}; | }; | ||||
this.handleMouseEnter(qualifier); | this.handleMouseEnter(qualifier); | ||||
}} | }} | ||||
> | > | ||||
<Spinner loading={loadingMore === qualifier}>{translate('show_more')}</Spinner> | |||||
<Spinner isLoading={loadingMore === qualifier}>{translate('show_more')}</Spinner> | |||||
</ItemButton> | </ItemButton> | ||||
); | ); | ||||
} | } |
* along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import { FlagMessage, Spinner } from 'design-system'; | |||||
import { Spinner } from '@sonarsource/echoes-react'; | |||||
import { FlagMessage } from 'design-system'; | |||||
import { findLastIndex, keyBy } from 'lodash'; | import { findLastIndex, keyBy } from 'lodash'; | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { getComponentForSourceViewer, getDuplications, getSources } from '../../../api/components'; | import { getComponentForSourceViewer, getDuplications, getSources } from '../../../api/components'; |
* along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import styled from '@emotion/styled'; | import styled from '@emotion/styled'; | ||||
import { Spinner } from '@sonarsource/echoes-react'; | |||||
import { | import { | ||||
LAYOUT_FOOTER_HEIGHT, | LAYOUT_FOOTER_HEIGHT, | ||||
LargeCenteredLayout, | LargeCenteredLayout, | ||||
PageContentFontWrapper, | PageContentFontWrapper, | ||||
Spinner, | |||||
themeBorder, | themeBorder, | ||||
themeColor, | themeColor, | ||||
} from 'design-system'; | } from 'design-system'; | ||||
import { translate } from '../../../helpers/l10n'; | import { translate } from '../../../helpers/l10n'; | ||||
import { addSideBarClass, removeSideBarClass } from '../../../helpers/pages'; | import { addSideBarClass, removeSideBarClass } from '../../../helpers/pages'; | ||||
import { get, save } from '../../../helpers/storage'; | import { get, save } from '../../../helpers/storage'; | ||||
import { isDefined } from '../../../helpers/types'; | |||||
import { AppState } from '../../../types/appstate'; | import { AppState } from '../../../types/appstate'; | ||||
import { ComponentQualifier } from '../../../types/component'; | import { ComponentQualifier } from '../../../types/component'; | ||||
import { MetricKey } from '../../../types/metrics'; | |||||
import { RawQuery } from '../../../types/types'; | import { RawQuery } from '../../../types/types'; | ||||
import { CurrentUser, isLoggedIn } from '../../../types/users'; | import { CurrentUser, isLoggedIn } from '../../../types/users'; | ||||
import { Query, hasFilterParams, parseUrlQuery } from '../query'; | import { Query, hasFilterParams, parseUrlQuery } from '../query'; | ||||
import ProjectsList from './ProjectsList'; | import ProjectsList from './ProjectsList'; | ||||
interface Props { | interface Props { | ||||
appState: AppState; | |||||
currentUser: CurrentUser; | currentUser: CurrentUser; | ||||
isFavorite: boolean; | isFavorite: boolean; | ||||
location: Location; | location: Location; | ||||
appState: AppState; | |||||
router: Router; | router: Router; | ||||
} | } | ||||
handlePerspectiveChange = ({ view }: { view?: string }) => { | handlePerspectiveChange = ({ view }: { view?: string }) => { | ||||
const query: { | const query: { | ||||
view: string | undefined; | view: string | undefined; | ||||
sort?: string | undefined; | |||||
sort?: string; | |||||
} = { | } = { | ||||
view: view === 'overall' ? undefined : view, | view: view === 'overall' ? undefined : view, | ||||
}; | }; | ||||
if (this.state.query.view === 'leak' || view === 'leak') { | if (this.state.query.view === 'leak' || view === 'leak') { | ||||
if (this.state.query.sort) { | |||||
if (isDefined(this.state.query.sort)) { | |||||
const sort = parseSorting(this.state.query.sort); | const sort = parseSorting(this.state.query.sort); | ||||
if (SORTING_SWITCH[sort.sortValue]) { | |||||
if (isDefined(SORTING_SWITCH[sort.sortValue])) { | |||||
query.sort = (sort.sortDesc ? '-' : '') + SORTING_SWITCH[sort.sortValue]; | query.sort = (sort.sortDesc ? '-' : '') + SORTING_SWITCH[sort.sortValue]; | ||||
} | } | ||||
} | } | ||||
this.props.router.push({ pathname: this.props.location.pathname, query }); | this.props.router.push({ pathname: this.props.location.pathname, query }); | ||||
} else { | } else { | ||||
this.updateLocationQuery(query); | this.updateLocationQuery(query); | ||||
return searchProjects(data).then(({ facets }) => { | return searchProjects(data).then(({ facets }) => { | ||||
const values = facets.find((facet) => facet.property === property)?.values ?? []; | const values = facets.find((facet) => facet.property === property)?.values ?? []; | ||||
return mapValues(keyBy(values, 'val'), 'count'); | return mapValues(keyBy(values, 'val'), 'count'); | ||||
}); | }); | ||||
}; | }; | ||||
handleFavorite={this.handleFavorite} | handleFavorite={this.handleFavorite} | ||||
isFavorite={this.props.isFavorite} | isFavorite={this.props.isFavorite} | ||||
isFiltered={hasFilterParams(this.state.query)} | isFiltered={hasFilterParams(this.state.query)} | ||||
loading={this.state.loading} | |||||
loadMore={this.fetchMoreProjects} | |||||
projects={this.state.projects} | projects={this.state.projects} | ||||
query={this.state.query} | query={this.state.query} | ||||
loadMore={this.fetchMoreProjects} | |||||
loading={this.state.loading} | |||||
total={this.state.total} | total={this.state.total} | ||||
/> | /> | ||||
)} | )} | ||||
render() { | render() { | ||||
return ( | return ( | ||||
<StyledWrapper id="projects-page"> | <StyledWrapper id="projects-page"> | ||||
<Suggestions suggestions="projects" /> | |||||
<Suggestions suggestions={MetricKey.projects} /> | |||||
<Helmet defer={false} title={translate('projects.page')} /> | <Helmet defer={false} title={translate('projects.page')} /> | ||||
<h1 className="sw-sr-only">{translate('projects.page')}</h1> | <h1 className="sw-sr-only">{translate('projects.page')}</h1> | ||||
view?: string; | view?: string; | ||||
} = {}; | } = {}; | ||||
if (get(LS_PROJECTS_SORT)) { | |||||
options.sort = get(LS_PROJECTS_SORT) || undefined; | |||||
if (get(LS_PROJECTS_SORT) !== null) { | |||||
options.sort = get(LS_PROJECTS_SORT) ?? undefined; | |||||
} | } | ||||
if (get(LS_PROJECTS_VIEW)) { | |||||
options.view = get(LS_PROJECTS_VIEW) || undefined; | |||||
if (get(LS_PROJECTS_VIEW) !== null) { | |||||
options.view = get(LS_PROJECTS_VIEW) ?? undefined; | |||||
} | } | ||||
return options; | return options; | ||||
} | } | ||||
function SetSearchParamsWrapper(props: Props) { | |||||
function SetSearchParamsWrapper(props: Readonly<Props>) { | |||||
const [searchParams, setSearchParams] = useSearchParams(); | const [searchParams, setSearchParams] = useSearchParams(); | ||||
const savedOptions = getStorageOptions(); | const savedOptions = getStorageOptions(); | ||||
React.useEffect( | React.useEffect( | ||||
() => { | () => { | ||||
const hasViewParams = searchParams.get('sort') || searchParams.get('view'); | |||||
const hasSavedOptions = savedOptions.sort || savedOptions.view; | |||||
const hasViewParams = searchParams.get('sort') ?? searchParams.get('view'); | |||||
const hasSavedOptions = savedOptions.sort ?? savedOptions.view; | |||||
if (!hasViewParams && hasSavedOptions) { | |||||
if (!isDefined(hasViewParams) && isDefined(hasSavedOptions)) { | |||||
setSearchParams(savedOptions); | setSearchParams(savedOptions); | ||||
} | } | ||||
}, | }, |
* along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import { ButtonPrimary, FlagMessage, Modal, Spinner } from 'design-system'; | |||||
import { Spinner } from '@sonarsource/echoes-react'; | |||||
import { ButtonPrimary, FlagMessage, Modal } from 'design-system'; | |||||
import { keyBy } from 'lodash'; | import { keyBy } from 'lodash'; | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { FormattedMessage } from 'react-intl'; | import { FormattedMessage } from 'react-intl'; | ||||
import { isAllowToSignUpEnabled, isOrganizationListEmpty } from './hook/useGithubConfiguration'; | import { isAllowToSignUpEnabled, isOrganizationListEmpty } from './hook/useGithubConfiguration'; | ||||
interface Props { | interface Props { | ||||
create: boolean; | |||||
loading: boolean; | |||||
values: Dict<SettingValue>; | |||||
setNewValue: (key: string, value: string | boolean) => void; | |||||
canBeSave: boolean; | canBeSave: boolean; | ||||
onClose: () => void; | |||||
tab: AuthenticationTabs; | |||||
create: boolean; | |||||
excludedField: string[]; | excludedField: string[]; | ||||
hasLegacyConfiguration?: boolean; | hasLegacyConfiguration?: boolean; | ||||
loading: boolean; | |||||
onClose: () => void; | |||||
provisioningStatus?: ProvisioningType; | provisioningStatus?: ProvisioningType; | ||||
setNewValue: (key: string, value: string | boolean) => void; | |||||
tab: AuthenticationTabs; | |||||
values: Dict<SettingValue>; | |||||
} | } | ||||
interface ErrorValue { | interface ErrorValue { | ||||
export default function ConfigurationForm(props: Readonly<Props>) { | export default function ConfigurationForm(props: Readonly<Props>) { | ||||
const { | const { | ||||
create, | |||||
loading, | |||||
values, | |||||
setNewValue, | |||||
canBeSave, | canBeSave, | ||||
tab, | |||||
create, | |||||
excludedField, | excludedField, | ||||
hasLegacyConfiguration, | hasLegacyConfiguration, | ||||
loading, | |||||
provisioningStatus, | provisioningStatus, | ||||
setNewValue, | |||||
tab, | |||||
values, | |||||
} = props; | } = props; | ||||
const [errors, setErrors] = React.useState<Dict<ErrorValue>>({}); | const [errors, setErrors] = React.useState<Dict<ErrorValue>>({}); | ||||
const [showConfirmModal, setShowConfirmModal] = React.useState(false); | const [showConfirmModal, setShowConfirmModal] = React.useState(false); | ||||
const errors = Object.values(values) | const errors = Object.values(values) | ||||
.filter((v) => v.newValue === undefined && v.value === undefined && v.mandatory) | .filter((v) => v.newValue === undefined && v.value === undefined && v.mandatory) | ||||
.map((v) => ({ key: v.key, message: translate('field_required') })); | .map((v) => ({ key: v.key, message: translate('field_required') })); | ||||
setErrors(keyBy(errors, 'key')); | setErrors(keyBy(errors, 'key')); | ||||
} | } | ||||
}; | }; | ||||
const onSave = async () => { | const onSave = async () => { | ||||
const data = await changeConfig(Object.values(values)); | const data = await changeConfig(Object.values(values)); | ||||
const errors = data | const errors = data | ||||
.filter(({ success }) => !success) | .filter(({ success }) => !success) | ||||
.map(({ key }) => ({ key, message: translate('default_save_field_error_message') })); | .map(({ key }) => ({ key, message: translate('default_save_field_error_message') })); | ||||
const formBody = ( | const formBody = ( | ||||
<form id={FORM_ID} onSubmit={handleSubmit}> | <form id={FORM_ID} onSubmit={handleSubmit}> | ||||
<Spinner loading={loading} ariaLabel={translate('settings.authentication.form.loading')}> | |||||
<Spinner ariaLabel={translate('settings.authentication.form.loading')} isLoading={loading}> | |||||
<FlagMessage | <FlagMessage | ||||
className="sw-w-full sw-mb-8" | className="sw-w-full sw-mb-8" | ||||
variant={hasLegacyConfiguration ? 'warning' : 'info'} | variant={hasLegacyConfiguration ? 'warning' : 'info'} | ||||
> | > | ||||
<span> | <span> | ||||
<FormattedMessage | <FormattedMessage | ||||
id={`settings.authentication.${helpMessage}`} | |||||
defaultMessage={translate(`settings.authentication.${helpMessage}`)} | defaultMessage={translate(`settings.authentication.${helpMessage}`)} | ||||
id={`settings.authentication.${helpMessage}`} | |||||
values={{ | values={{ | ||||
link: ( | link: ( | ||||
<DocumentationLink | <DocumentationLink | ||||
/> | /> | ||||
</span> | </span> | ||||
</FlagMessage> | </FlagMessage> | ||||
{Object.values(values).map((val) => { | {Object.values(values).map((val) => { | ||||
if (excludedField.includes(val.key)) { | if (excludedField.includes(val.key)) { | ||||
return null; | return null; | ||||
} | } | ||||
const isSet = hasLegacyConfiguration ? false : !val.isNotSet; | const isSet = hasLegacyConfiguration ? false : !val.isNotSet; | ||||
return ( | return ( | ||||
<div key={val.key} className="sw-mb-8"> | <div key={val.key} className="sw-mb-8"> | ||||
<AuthenticationFormField | <AuthenticationFormField | ||||
settingValue={values[val.key]?.newValue ?? values[val.key]?.value} | |||||
definition={val.definition} | definition={val.definition} | ||||
error={errors[val.key]?.message} | |||||
isNotSet={!isSet} | |||||
mandatory={val.mandatory} | mandatory={val.mandatory} | ||||
onFieldChange={setNewValue} | onFieldChange={setNewValue} | ||||
isNotSet={!isSet} | |||||
error={errors[val.key]?.message} | |||||
settingValue={values[val.key]?.newValue ?? values[val.key]?.value} | |||||
/> | /> | ||||
</div> | </div> | ||||
); | ); | ||||
return ( | return ( | ||||
<> | <> | ||||
<Modal | <Modal | ||||
body={formBody} | |||||
headerTitle={header} | headerTitle={header} | ||||
isScrollable | isScrollable | ||||
onClose={props.onClose} | onClose={props.onClose} | ||||
body={formBody} | |||||
primaryButton={ | primaryButton={ | ||||
<ButtonPrimary form={FORM_ID} type="submit" autoFocus disabled={!canBeSave}> | <ButtonPrimary form={FORM_ID} type="submit" autoFocus disabled={!canBeSave}> | ||||
{translate('settings.almintegration.form.save')} | {translate('settings.almintegration.form.save')} | ||||
<Spinner className="sw-ml-2" loading={loading} /> | |||||
<Spinner className="sw-ml-2" isLoading={loading} /> | |||||
</ButtonPrimary> | </ButtonPrimary> | ||||
} | } | ||||
/> | /> | ||||
{showConfirmModal && ( | {showConfirmModal && ( | ||||
<GitHubConfirmModal | <GitHubConfirmModal | ||||
onConfirm={onSave} | |||||
onClose={() => setShowConfirmModal(false)} | onClose={() => setShowConfirmModal(false)} | ||||
values={values} | |||||
onConfirm={onSave} | |||||
provisioningStatus={provisioningStatus ?? ProvisioningType.jit} | provisioningStatus={provisioningStatus ?? ProvisioningType.jit} | ||||
values={values} | |||||
/> | /> | ||||
)} | )} | ||||
</> | </> |