diff --git a/src/_nav.jsx b/src/_nav.jsx
index 2f99dffd0953..902a8390b79f 100644
--- a/src/_nav.jsx
+++ b/src/_nav.jsx
@@ -150,7 +150,7 @@ const _nav = [
{
component: CNavItem,
name: 'Tenant Onboarding',
- to: '/tenant/administration/tenant-onboarding-wizard',
+ to: '/tenant/administration/tenant-onboarding',
},
{
component: CNavItem,
diff --git a/src/components/tables/CippTable.jsx b/src/components/tables/CippTable.jsx
index c977c700cc87..f3ea486ee243 100644
--- a/src/components/tables/CippTable.jsx
+++ b/src/components/tables/CippTable.jsx
@@ -125,6 +125,7 @@ export default function CippTable({
filterlist,
showFilter = true,
endpointName,
+ defaultSortAsc = true,
tableProps: {
keyField = 'id',
theme = 'cyberdrain',
@@ -989,7 +990,7 @@ export default function CippTable({
expandableRowsComponent={expandableRowsComponent}
highlightOnHover={highlightOnHover}
expandOnRowClicked={expandOnRowClicked}
- defaultSortAsc
+ defaultSortAsc={defaultSortAsc}
defaultSortFieldId={1}
sortFunction={customSort}
paginationPerPage={tablePageSize}
@@ -1050,6 +1051,7 @@ export const CippTablePropTypes = {
disableCSVExport: PropTypes.bool,
error: PropTypes.object,
filterlist: PropTypes.arrayOf(PropTypes.object),
+ defaultSortAsc: PropTypes.bool,
}
CippTable.propTypes = CippTablePropTypes
diff --git a/src/importsMap.jsx b/src/importsMap.jsx
index 7b7589049b42..fd44014ae9c7 100644
--- a/src/importsMap.jsx
+++ b/src/importsMap.jsx
@@ -140,6 +140,7 @@ import React from 'react'
"/tenant/administration/gdap-status": React.lazy(() => import('./views/tenant/administration/ListGDAPQueue')),
"/tenant/standards/list-standards": React.lazy(() => import('./views/tenant/standards/ListStandards')),
"/tenant/administration/tenant-offboarding-wizard": React.lazy(() => import('./views/tenant/administration/TenantOffboardingWizard')),
+ "/tenant/administration/tenant-onboarding": React.lazy(() => import('./views/tenant/administration/TenantOnboarding')),
"/tenant/administration/tenant-onboarding-wizard": React.lazy(() => import('./views/tenant/administration/TenantOnboardingWizard')),
}
export default importsMap
\ No newline at end of file
diff --git a/src/routes.json b/src/routes.json
index e1d382459781..e681555e0346 100644
--- a/src/routes.json
+++ b/src/routes.json
@@ -954,6 +954,12 @@
"component": "views/tenant/administration/TenantOffboardingWizard",
"allowedRoles": ["admin"]
},
+ {
+ "path": "/tenant/administration/tenant-onboarding",
+ "name": "Tenant Onboarding",
+ "component": "views/tenant/administration/TenantOnboarding",
+ "allowedRoles": ["admin"]
+ },
{
"path": "/tenant/administration/tenant-onboarding-wizard",
"name": "Tenant Onboarding",
diff --git a/src/views/tenant/administration/TenantOnboarding.jsx b/src/views/tenant/administration/TenantOnboarding.jsx
new file mode 100644
index 000000000000..94afd7437938
--- /dev/null
+++ b/src/views/tenant/administration/TenantOnboarding.jsx
@@ -0,0 +1,153 @@
+import { CBadge, CTooltip } from '@coreui/react'
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
+import React from 'react'
+import { TitleButton } from 'src/components/buttons'
+import { CippPageList } from 'src/components/layout'
+import { CellBadge, cellDateFormatter } from 'src/components/tables'
+import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat'
+
+const TenantOnboarding = () => {
+ const titleButton = (
+
+ )
+ function ucfirst(str) {
+ return str.charAt(0).toUpperCase() + str.slice(1)
+ }
+ function getBadgeColor(status) {
+ switch (status.toLowerCase()) {
+ case 'queued':
+ return 'info'
+ case 'failed':
+ return 'danger'
+ case 'succeeded':
+ return 'success'
+ case 'running':
+ return 'primary'
+ }
+ }
+ function getLatestStep(steps) {
+ var activeSteps = steps?.filter((step) => step.Status !== 'pending')
+ var currentStep = activeSteps[activeSteps.length - 1]
+ var color = 'info'
+ var icon = 'me-2 info-circle'
+ var spin = false
+ switch (currentStep?.Status) {
+ case 'succeeded':
+ color = 'me-2 text-success'
+ icon = 'check-circle'
+ break
+ case 'failed':
+ color = 'me-2 text-danger'
+ icon = 'times-circle'
+ break
+ case 'running':
+ color = 'me-2 text-primary'
+ icon = 'sync'
+ spin = true
+ break
+ }
+ return (
+
+
+
+ {currentStep?.Title}
+
+
+ )
+ }
+ const columns = [
+ {
+ name: 'Last Update',
+ selector: (row) => row.Timestamp,
+ sortable: true,
+ exportSelector: 'Timestamp',
+ cell: cellDateFormatter({ format: 'short' }),
+ },
+ {
+ name: 'Tenant',
+ selector: (row) => row?.Relationship?.customer?.displayName,
+ sortable: true,
+ cell: cellGenericFormatter(),
+ exportSelector: 'Relationship/customer/displayName',
+ },
+ {
+ name: 'Status',
+ selector: (row) => row?.Status,
+ sortable: true,
+ exportSelector: 'Status',
+ cell: (row) => CellBadge({ label: ucfirst(row?.Status), color: getBadgeColor(row?.Status) }),
+ },
+ {
+ name: 'Onboarding Step',
+ selector: (row) => row?.OnboardingSteps,
+ cell: (row) => getLatestStep(row?.OnboardingSteps),
+ },
+ {
+ name: 'Logs',
+ selector: (row) => row?.Logs,
+ sortable: false,
+ cell: cellGenericFormatter(),
+ },
+ ]
+ return (
+
+
+
+ )
+}
+
+export default TenantOnboarding
diff --git a/src/views/tenant/administration/TenantOnboardingWizard.jsx b/src/views/tenant/administration/TenantOnboardingWizard.jsx
index 122765dff6c3..874510c3517e 100644
--- a/src/views/tenant/administration/TenantOnboardingWizard.jsx
+++ b/src/views/tenant/administration/TenantOnboardingWizard.jsx
@@ -1,32 +1,16 @@
-import React, { useState, useRef, useEffect } from 'react'
-import {
- CAccordion,
- CAccordionBody,
- CAccordionHeader,
- CAccordionItem,
- CButton,
- CCallout,
- CCol,
- CRow,
- CSpinner,
-} from '@coreui/react'
+import React, { useRef, useEffect } from 'react'
+import { CAccordion, CCallout, CCol, CRow } from '@coreui/react'
import { Field, FormSpy } from 'react-final-form'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faExclamationTriangle, faTimes, faCheck } from '@fortawesome/free-solid-svg-icons'
import { useSelector } from 'react-redux'
import { CippWizard } from 'src/components/layout'
import PropTypes from 'prop-types'
-import { RFFCFormCheck, RFFCFormInput, RFFCFormSwitch, RFFSelectSearch } from 'src/components/forms'
-import { CippCodeBlock, TenantSelector } from 'src/components/utilities'
+import { RFFCFormSwitch } from 'src/components/forms'
import { useLazyGenericPostRequestQuery } from 'src/store/api/app'
-import {
- CellDate,
- WizardTableField,
- cellDateFormatter,
- cellNullTextFormatter,
-} from 'src/components/tables'
-import ReactTimeAgo from 'react-time-ago'
-import { TableModalButton, TitleButton } from 'src/components/buttons'
+import { WizardTableField, cellDateFormatter, cellNullTextFormatter } from 'src/components/tables'
+import { TitleButton } from 'src/components/buttons'
+import RelationshipOnboarding from 'src/views/tenant/administration/onboarding/RelationshipOnboarding'
const Error = ({ name }) => (
{
- const [relationshipReady, setRelationshipReady] = useState(false)
- const [refreshGuid, setRefreshGuid] = useState(false)
- const [getOnboardingStatus, onboardingStatus] = useLazyGenericPostRequestQuery()
- var headerIcon = relationshipReady ? 'check-circle' : 'question-circle'
-
- useInterval(
- async () => {
- if (onboardingStatus.data?.Status == 'running' || onboardingStatus.data?.Status == 'queued') {
- getOnboardingStatus({
- path: '/api/ExecOnboardTenant',
- values: { id: relationship.id },
- })
- }
- },
- 5000,
- onboardingStatus.data,
- )
-
- return (
-
-
- {onboardingStatus?.data?.Status == 'running' ? (
-
- ) : (
-
- )}
- Onboarding Relationship: {}
- {relationship.displayName}
-
-
-
- {(relationship?.customer?.displayName ||
- onboardingStatus?.data?.Relationship?.customer?.displayName) && (
-
- Customer
- {onboardingStatus?.data?.Relationship?.customer?.displayName
- ? onboardingStatus?.data?.Relationship?.customer?.displayName
- : relationship.customer.displayName}
-
- )}
- {onboardingStatus?.data?.Timestamp && (
-
- Last Updated
-
-
- )}
-
- Relationship Status
- {relationship.status}
-
-
- Creation Date
-
-
- {relationship.status == 'approvalPending' &&
- onboardingStatus?.data?.Relationship?.status != 'active' && (
-
- Invite URL
-
-
- )}
-
- {onboardingStatus.isUninitialized &&
- getOnboardingStatus({
- path: '/api/ExecOnboardTenant',
- values: { id: relationship.id, gdapRoles, autoMapRoles, addMissingGroups },
- })}
- {onboardingStatus.isSuccess && (
- <>
- {onboardingStatus.data?.Status != 'queued' && (
-
- getOnboardingStatus({
- path: '/api/ExecOnboardTenant?Retry=True',
- values: { id: relationship.id, gdapRoles, autoMapRoles, addMissingGroups },
- })
- }
- className="mb-3 me-2"
- >
- Retry
-
- )}
- {onboardingStatus.data?.Logs && (
-
- )}
-
- {onboardingStatus.data?.OnboardingSteps?.map((step, idx) => (
-
-
- {step.Status == 'running' ? (
-
- ) : (
-
- )}{' '}
- {step.Title}
-
-
- {step.Message}
-
-
- ))}
- >
- )}
-
-
- )
-}
-RelationshipOnboarding.propTypes = {
- relationship: PropTypes.object.isRequired,
- gdapRoles: PropTypes.array,
- autoMapRoles: PropTypes.bool,
- addMissingGroups: PropTypes.bool,
-}
-
const TenantOnboardingWizard = () => {
const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName)
const currentSettings = useSelector((state) => state.app)
@@ -350,6 +183,11 @@ const TenantOnboardingWizard = () => {
Tenant Onboarding Options
+ Optional Settings
+
+ Use these options for relationships created outside of the CIPP Invite Wizard or if the
+ SAM user is missing required GDAP groups from the Partner Tenant.
+
diff --git a/src/views/tenant/administration/onboarding/RelationshipOnboarding.jsx b/src/views/tenant/administration/onboarding/RelationshipOnboarding.jsx
new file mode 100644
index 000000000000..3f5c93f4ab12
--- /dev/null
+++ b/src/views/tenant/administration/onboarding/RelationshipOnboarding.jsx
@@ -0,0 +1,195 @@
+import React, { useState, useRef, useEffect } from 'react'
+import {
+ CAccordionBody,
+ CAccordionHeader,
+ CAccordionItem,
+ CButton,
+ CCallout,
+ CCol,
+ CRow,
+ CSpinner,
+} from '@coreui/react'
+import { Field } from 'react-final-form'
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
+import { faExclamationTriangle, faTimes, faCheck } from '@fortawesome/free-solid-svg-icons'
+import PropTypes from 'prop-types'
+import { CippCodeBlock, TenantSelector } from 'src/components/utilities'
+import { useLazyGenericPostRequestQuery } from 'src/store/api/app'
+import { CellDate } from 'src/components/tables'
+import ReactTimeAgo from 'react-time-ago'
+import { TableModalButton, TitleButton } from 'src/components/buttons'
+
+function useInterval(callback, delay, state) {
+ const savedCallback = useRef()
+
+ // Remember the latest callback.
+ useEffect(() => {
+ savedCallback.current = callback
+ })
+
+ // Set up the interval.
+ useEffect(() => {
+ function tick() {
+ savedCallback.current()
+ }
+
+ if (delay !== null) {
+ let id = setInterval(tick, delay)
+ return () => clearInterval(id)
+ }
+ }, [delay, state])
+}
+
+const RelationshipOnboarding = ({ relationship, gdapRoles, autoMapRoles, addMissingGroups }) => {
+ const [relationshipReady, setRelationshipReady] = useState(false)
+ const [refreshGuid, setRefreshGuid] = useState(false)
+ const [getOnboardingStatus, onboardingStatus] = useLazyGenericPostRequestQuery()
+ var headerIcon = relationshipReady ? 'check-circle' : 'question-circle'
+
+ useInterval(
+ async () => {
+ if (onboardingStatus.data?.Status == 'running' || onboardingStatus.data?.Status == 'queued') {
+ getOnboardingStatus({
+ path: `/api/ExecOnboardTenant`,
+ values: { id: relationship.id },
+ })
+ }
+ },
+ 5000,
+ onboardingStatus.data,
+ )
+
+ return (
+
+
+ {onboardingStatus?.data?.Status == 'running' ? (
+
+ ) : (
+
+ )}
+ Onboarding Relationship: {}
+ {relationship.displayName}
+
+
+
+ {(relationship?.customer?.displayName ||
+ onboardingStatus?.data?.Relationship?.customer?.displayName) && (
+
+ Customer
+ {onboardingStatus?.data?.Relationship?.customer?.displayName
+ ? onboardingStatus?.data?.Relationship?.customer?.displayName
+ : relationship.customer.displayName}
+
+ )}
+ {onboardingStatus?.data?.Timestamp && (
+
+ Last Updated
+
+
+ )}
+
+ Relationship Status
+ {relationship.status}
+
+
+ Creation Date
+
+
+ {relationship.status == 'approvalPending' &&
+ onboardingStatus?.data?.Relationship?.status != 'active' && (
+
+ Invite URL
+
+
+ )}
+
+ {onboardingStatus.isUninitialized &&
+ getOnboardingStatus({
+ path: '/api/ExecOnboardTenant',
+ values: { id: relationship.id, gdapRoles, autoMapRoles, addMissingGroups },
+ })}
+ {onboardingStatus.isSuccess && (
+ <>
+ {onboardingStatus.data?.Status != 'queued' && (
+
+ getOnboardingStatus({
+ path: '/api/ExecOnboardTenant?Retry=True',
+ values: { id: relationship.id, gdapRoles, autoMapRoles, addMissingGroups },
+ })
+ }
+ className="mb-3 me-2"
+ >
+ Retry
+
+ )}
+ {onboardingStatus.data?.Logs && (
+
+ )}
+
+ {onboardingStatus.data?.OnboardingSteps?.map((step, idx) => (
+
+
+ {step.Status == 'running' ? (
+
+ ) : (
+
+ )}{' '}
+ {step.Title}
+
+
+ {step.Message}
+
+
+ ))}
+ >
+ )}
+
+
+ )
+}
+RelationshipOnboarding.propTypes = {
+ relationship: PropTypes.object.isRequired,
+ gdapRoles: PropTypes.array,
+ autoMapRoles: PropTypes.bool,
+ addMissingGroups: PropTypes.bool,
+ statusOnly: PropTypes.bool,
+}
+
+export default RelationshipOnboarding