implement progress steps component
This commit is contained in:
parent
8822d479a0
commit
bc1a072515
9 changed files with 318 additions and 73 deletions
|
@ -26,7 +26,7 @@ export const Select = ({ control, name, label, options, disabled = false }: Sele
|
||||||
value={field.value ? field.value : ''} // input value
|
value={field.value ? field.value : ''} // input value
|
||||||
name={name} // send down the input name
|
name={name} // send down the input name
|
||||||
ref={field.ref} // send input ref, so we can focus on input when error appear
|
ref={field.ref} // send input ref, so we can focus on input when error appear
|
||||||
className="shadow-sm focus:ring-primary-500 focus:border-primary-500 block w-full sm:text-sm border-gray-300 rounded-md"
|
className="block shadow-sm focus:ring-primary-500 focus:border-primary-500 block w-full sm:text-sm border-gray-300 rounded-md"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
{options?.map((option) => (
|
{options?.map((option) => (
|
||||||
|
|
|
@ -12,6 +12,7 @@ export const Modal: React.FC<ModalProps> = ({
|
||||||
useCancelButton = false,
|
useCancelButton = false,
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
leftActions = <></>,
|
leftActions = <></>,
|
||||||
|
saveButtonDisabled = false,
|
||||||
}) => {
|
}) => {
|
||||||
const cancelButtonRef = useRef(null);
|
const cancelButtonRef = useRef(null);
|
||||||
const saveButtonRef = useRef(null);
|
const saveButtonRef = useRef(null);
|
||||||
|
@ -86,9 +87,12 @@ export const Modal: React.FC<ModalProps> = ({
|
||||||
<div className="ml-auto sm:flex sm:flex-row-reverse">
|
<div className="ml-auto sm:flex sm:flex-row-reverse">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-primary-600 text-base font-medium text-white hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 sm:ml-3 sm:w-auto sm:text-sm"
|
className={`w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-primary-600 text-base font-medium text-white hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 sm:ml-3 sm:w-auto sm:text-sm ${
|
||||||
|
saveButtonDisabled ? 'opacity-50' : ''
|
||||||
|
}`}
|
||||||
onClick={onSave}
|
onClick={onSave}
|
||||||
ref={saveButtonRef}
|
ref={saveButtonRef}
|
||||||
|
disabled={saveButtonDisabled}
|
||||||
>
|
>
|
||||||
Save Changes
|
Save Changes
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -8,4 +8,5 @@ export type ModalProps = {
|
||||||
useCancelButton?: boolean;
|
useCancelButton?: boolean;
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
leftActions?: React.ReactNode;
|
leftActions?: React.ReactNode;
|
||||||
|
saveButtonDisabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
107
src/components/Steps/ProgressSteps.tsx
Normal file
107
src/components/Steps/ProgressSteps.tsx
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
import _ from 'lodash';
|
||||||
|
import React from 'react';
|
||||||
|
import { ProgressStepsProps, ProgressStepStatus } from './types';
|
||||||
|
|
||||||
|
export const ProgressSteps: React.FC<ProgressStepsProps> = ({ steps, onNext, onPrevious, onStepClick, children }) => {
|
||||||
|
const handleNext = () => {
|
||||||
|
if (onNext) {
|
||||||
|
onNext();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handlePrevious = () => {
|
||||||
|
if (onPrevious) {
|
||||||
|
onPrevious();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const showNextPage = () => {
|
||||||
|
if (onNext) {
|
||||||
|
return _.some(steps, { status: ProgressStepStatus.Upcoming });
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const showPreviousPage = () => {
|
||||||
|
if (onPrevious) {
|
||||||
|
return _.some(steps, { status: ProgressStepStatus.Complete });
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStepClick = (stepId: string) => {
|
||||||
|
if (onStepClick) {
|
||||||
|
onStepClick(stepId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<nav aria-label="Progress">
|
||||||
|
{/* eslint-disable-next-line jsx-a11y/no-redundant-roles */}
|
||||||
|
<ol role="list" className="space-y-4 md:flex md:space-y-0 md:space-x-8 mb-4">
|
||||||
|
{steps.map((step) => (
|
||||||
|
<li key={step.name} className="md:flex-1" onClick={() => handleStepClick(step.id)}>
|
||||||
|
{step.status === ProgressStepStatus.Complete ? (
|
||||||
|
<a
|
||||||
|
href={step.href}
|
||||||
|
className="group pl-4 py-2 flex flex-col border-l-4 border-primary-500 md:pl-0 md:pb-0 md:border-l-0 md:border-t-4"
|
||||||
|
>
|
||||||
|
<span className="text-xs text-primary-600 font-semibold tracking-wide uppercase group-hover:text-primary-800">
|
||||||
|
{step.id}
|
||||||
|
</span>
|
||||||
|
<span className="text-sm font-medium">{step.name}</span>
|
||||||
|
</a>
|
||||||
|
) : step.status === ProgressStepStatus.Current ? (
|
||||||
|
<a
|
||||||
|
href={step.href}
|
||||||
|
className="pl-4 py-2 flex flex-col border-l-4 border-primary-500 md:pl-0 md:pb-0 md:border-l-0 md:border-t-4"
|
||||||
|
aria-current="step"
|
||||||
|
>
|
||||||
|
<span className="text-xs text-primary-600 font-semibold tracking-wide uppercase">{step.id}</span>
|
||||||
|
<span className="text-sm font-medium">{step.name}</span>
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<a
|
||||||
|
href={step.href}
|
||||||
|
className="group pl-4 py-2 flex flex-col border-l-4 border-gray-200 hover:border-gray-300 md:pl-0 md:pb-0 md:border-l-0 md:border-t-4"
|
||||||
|
>
|
||||||
|
<span className="text-xs text-gray-500 font-semibold tracking-wide uppercase group-hover:text-gray-700">
|
||||||
|
{step.id}
|
||||||
|
</span>
|
||||||
|
<span className="text-sm font-medium">{step.name}</span>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{children}
|
||||||
|
|
||||||
|
{(showNextPage() || showPreviousPage()) && (
|
||||||
|
<div className="pt-4 sm sm:flex">
|
||||||
|
<div className="ml-auto sm:flex sm:flex-row-reverse">
|
||||||
|
{showNextPage() && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-primary-600 text-base font-medium text-white hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 sm:ml-3 sm:w-auto sm:text-sm"
|
||||||
|
onClick={handleNext}
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{showPreviousPage() && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-primary-600 text-base font-medium text-white hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 sm:ml-3 sm:w-auto sm:text-sm"
|
||||||
|
onClick={handlePrevious}
|
||||||
|
>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
1
src/components/Steps/index.ts
Normal file
1
src/components/Steps/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { ProgressSteps } from './ProgressSteps';
|
20
src/components/Steps/types.ts
Normal file
20
src/components/Steps/types.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
export type ProgressStepsProps = {
|
||||||
|
steps: ProgressStepInfo[];
|
||||||
|
onNext?: () => void;
|
||||||
|
onPrevious?: () => void;
|
||||||
|
onStepClick?: (stepId: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface ProgressStepInfo {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
status: ProgressStepStatus;
|
||||||
|
component?: React.ReactNode;
|
||||||
|
href?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ProgressStepStatus {
|
||||||
|
Complete = 0,
|
||||||
|
Current = 1,
|
||||||
|
Upcoming = 2,
|
||||||
|
}
|
|
@ -24,12 +24,12 @@ export const appAccessList = [
|
||||||
];
|
];
|
||||||
|
|
||||||
export const allAppAccessList = [
|
export const allAppAccessList = [
|
||||||
...appAccessList,
|
|
||||||
{
|
{
|
||||||
name: 'dashboard',
|
name: 'dashboard',
|
||||||
image: '/assets/logo-small.svg',
|
image: '/assets/logo-small.svg',
|
||||||
label: 'Dashboard',
|
label: 'Dashboard',
|
||||||
},
|
},
|
||||||
|
...appAccessList,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const initialAppRoles = [
|
export const initialAppRoles = [
|
||||||
|
|
|
@ -5,3 +5,4 @@ export { Banner } from './Banner';
|
||||||
export { Tabs } from './Tabs';
|
export { Tabs } from './Tabs';
|
||||||
export { Modal, ConfirmationModal } from './Modal';
|
export { Modal, ConfirmationModal } from './Modal';
|
||||||
export { UserModal } from './UserModal';
|
export { UserModal } from './UserModal';
|
||||||
|
export { ProgressSteps } from './Steps';
|
||||||
|
|
|
@ -1,25 +1,172 @@
|
||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { useFieldArray, useForm } from 'react-hook-form';
|
import { useFieldArray, useForm, useWatch } from 'react-hook-form';
|
||||||
|
|
||||||
import { Modal, Tabs } from 'src/components';
|
import { Banner, Modal, ProgressSteps } from 'src/components';
|
||||||
import { Select, TextArea } from 'src/components/Form';
|
import { Select, TextArea } from 'src/components/Form';
|
||||||
import { MultipleUsersData, UserRole, useUsers } from 'src/services/users';
|
import { MultipleUsersData, UserRole, useUsers } from 'src/services/users';
|
||||||
import { allAppAccessList } from 'src/components/UserModal/consts';
|
import { allAppAccessList } from 'src/components/UserModal/consts';
|
||||||
|
import { ProgressStepInfo, ProgressStepStatus } from 'src/components/Steps/types';
|
||||||
import { initialMultipleUsersForm, MultipleUsersModalProps } from './types';
|
import { initialMultipleUsersForm, MultipleUsersModalProps } from './types';
|
||||||
|
|
||||||
export const MultipleUsersModal = ({ open, onClose }: MultipleUsersModalProps) => {
|
export const MultipleUsersModal = ({ open, onClose }: MultipleUsersModalProps) => {
|
||||||
|
const [steps, setSteps] = useState<ProgressStepInfo[]>([]);
|
||||||
|
const [isAdminRoleSelected, setAdminRoleSelected] = useState(false);
|
||||||
const { createUsers, userModalLoading } = useUsers();
|
const { createUsers, userModalLoading } = useUsers();
|
||||||
|
|
||||||
const { control, handleSubmit } = useForm<MultipleUsersData>({
|
const { control, handleSubmit } = useForm<MultipleUsersData>({
|
||||||
defaultValues: initialMultipleUsersForm,
|
defaultValues: initialMultipleUsersForm,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { fields } = useFieldArray({
|
const { fields, update } = useFieldArray({
|
||||||
control,
|
control,
|
||||||
name: 'appRoles',
|
name: 'appRoles',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const dashboardRole = useWatch({
|
||||||
|
control,
|
||||||
|
name: 'appRoles.0.role',
|
||||||
|
});
|
||||||
|
|
||||||
|
const csvDataWatch = useWatch({
|
||||||
|
control,
|
||||||
|
name: 'csvUserData',
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const isAdminDashboardRoleSelected = dashboardRole === UserRole.Admin;
|
||||||
|
setAdminRoleSelected(isAdminDashboardRoleSelected);
|
||||||
|
if (isAdminDashboardRoleSelected) {
|
||||||
|
fields.forEach((field, index) => update(index, { name: field.name, role: UserRole.Admin }));
|
||||||
|
} else {
|
||||||
|
fields.forEach((field, index) => update(index, { name: field.name, role: UserRole.User }));
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [dashboardRole]);
|
||||||
|
|
||||||
|
const renderUsersCsvDataInput = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="mt-8">
|
||||||
|
<h3 className="text-lg leading-6 font-medium text-gray-900">CSV data</h3>
|
||||||
|
</div>
|
||||||
|
<div className="mt-6">
|
||||||
|
<TextArea
|
||||||
|
control={control}
|
||||||
|
name="csvUserData"
|
||||||
|
placeholder={`Please paste users in CSV format: email, name\nuser1@example.com,User One\nuser2@example.com,User Two`}
|
||||||
|
rows={15}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderAppAccess = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="mt-8">
|
||||||
|
<h3 className="text-lg leading-6 font-medium text-gray-900">App Access</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isAdminRoleSelected && (
|
||||||
|
<div className="sm:col-span-6">
|
||||||
|
<Banner title="Admin users automatically have admin-level access to all apps." titleSm="Admin user" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div className="flow-root mt-6">
|
||||||
|
<ul className="-my-5 divide-y divide-gray-200">
|
||||||
|
{fields
|
||||||
|
.filter((field) => field.name === 'dashboard')
|
||||||
|
.map((item, index) => (
|
||||||
|
<li className="py-4" key={item.name}>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div className="flex-shrink-0 flex-1 flex items-center">
|
||||||
|
<img
|
||||||
|
className="h-10 w-10 rounded-md overflow-hidden"
|
||||||
|
src={_.find(allAppAccessList, ['name', item.name!])?.image}
|
||||||
|
alt={item.name ?? 'Image'}
|
||||||
|
/>
|
||||||
|
<h3 className="ml-4 text-md leading-6 font-medium text-gray-900">
|
||||||
|
{_.find(allAppAccessList, ['name', item.name!])?.label}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div className="sm:col-span-2">
|
||||||
|
<Select
|
||||||
|
key={item.id}
|
||||||
|
control={control}
|
||||||
|
name={`appRoles.${index}.role`}
|
||||||
|
options={[
|
||||||
|
{ value: UserRole.User, name: 'User' },
|
||||||
|
{ value: UserRole.Admin, name: 'Admin' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
{!isAdminRoleSelected &&
|
||||||
|
fields.map((item, index) => {
|
||||||
|
if (item.name === 'dashboard') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li className="py-4" key={item.name}>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div className="flex-shrink-0 flex-1 flex items-center">
|
||||||
|
<img
|
||||||
|
className="h-10 w-10 rounded-md overflow-hidden"
|
||||||
|
src={_.find(allAppAccessList, ['name', item.name!])?.image}
|
||||||
|
alt={item.name ?? 'Image'}
|
||||||
|
/>
|
||||||
|
<h3 className="ml-4 text-md leading-6 font-medium text-gray-900">
|
||||||
|
{_.find(allAppAccessList, ['name', item.name!])?.label}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div className="sm:col-span-2">
|
||||||
|
<Select
|
||||||
|
key={item.id}
|
||||||
|
control={control}
|
||||||
|
name={`appRoles.${index}.role`}
|
||||||
|
disabled={isAdminRoleSelected}
|
||||||
|
options={[
|
||||||
|
{ value: UserRole.NoAccess, name: 'No Access' },
|
||||||
|
{ value: UserRole.User, name: 'User' },
|
||||||
|
{ value: UserRole.Admin, name: 'Admin' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSteps([
|
||||||
|
{
|
||||||
|
id: 'Step 1',
|
||||||
|
name: 'Enter CSV user data',
|
||||||
|
status: ProgressStepStatus.Current,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Step 2',
|
||||||
|
name: 'Define app access roles',
|
||||||
|
status: ProgressStepStatus.Upcoming,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
try {
|
try {
|
||||||
await handleSubmit((data) => createUsers(data))();
|
await handleSubmit((data) => createUsers(data))();
|
||||||
|
@ -34,83 +181,47 @@ export const MultipleUsersModal = ({ open, onClose }: MultipleUsersModalProps) =
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderUsersCsvDataInput = () => {
|
const updateStepsStatus = (nextIndex: number) => {
|
||||||
return (
|
const updatedSteps = [...steps];
|
||||||
<div>
|
_.forEach(updatedSteps, (step, index) => {
|
||||||
<TextArea
|
if (index < nextIndex) {
|
||||||
control={control}
|
step.status = ProgressStepStatus.Complete;
|
||||||
name="csvUserData"
|
} else if (index === nextIndex) {
|
||||||
label="CSV user data"
|
step.status = ProgressStepStatus.Current;
|
||||||
placeholder="Please paste users in CSV format: email,name"
|
} else {
|
||||||
rows={15}
|
step.status = ProgressStepStatus.Upcoming;
|
||||||
required
|
}
|
||||||
/>
|
});
|
||||||
</div>
|
setSteps(updatedSteps);
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderAppAccess = () => {
|
const handleStepClick = (stepId: string) => {
|
||||||
return (
|
const activeStepIndex = _.findIndex(steps, { id: stepId });
|
||||||
<div>
|
updateStepsStatus(activeStepIndex);
|
||||||
<div className="mt-8">
|
|
||||||
<h3 className="text-lg leading-6 font-medium text-gray-900">App Access</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div className="flow-root mt-6">
|
|
||||||
<ul className="-my-5 divide-y divide-gray-200 ">
|
|
||||||
{fields.map((item, index) => {
|
|
||||||
return (
|
|
||||||
<li className="py-4" key={item.name}>
|
|
||||||
<div className="flex items-center space-x-4">
|
|
||||||
<div className="flex-shrink-0 flex-1 flex items-center">
|
|
||||||
<img
|
|
||||||
className="h-10 w-10 rounded-md overflow-hidden"
|
|
||||||
src={_.find(allAppAccessList, ['name', item.name!])?.image}
|
|
||||||
alt={item.name ?? 'Image'}
|
|
||||||
/>
|
|
||||||
<h3 className="ml-4 text-md leading-6 font-medium text-gray-900">
|
|
||||||
{_.find(allAppAccessList, ['name', item.name!])?.label}
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Select
|
|
||||||
key={item.id}
|
|
||||||
control={control}
|
|
||||||
name={`appRoles.${index}.role`}
|
|
||||||
options={[
|
|
||||||
{ value: UserRole.NoAccess, name: 'No Access' },
|
|
||||||
{ value: UserRole.User, name: 'User' },
|
|
||||||
{ value: UserRole.Admin, name: 'Admin' },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getActiveStepIndex = _.findIndex(steps, { status: ProgressStepStatus.Current });
|
||||||
|
const disableSave = _.isEmpty(csvDataWatch) || _.some(steps, { status: ProgressStepStatus.Upcoming });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal onClose={handleClose} open={open} onSave={handleSave} isLoading={userModalLoading} useCancelButton>
|
<Modal
|
||||||
|
onClose={handleClose}
|
||||||
|
open={open}
|
||||||
|
onSave={handleSave}
|
||||||
|
isLoading={userModalLoading}
|
||||||
|
useCancelButton
|
||||||
|
saveButtonDisabled={disableSave}
|
||||||
|
>
|
||||||
<div className="bg-white px-4">
|
<div className="bg-white px-4">
|
||||||
<div className="space-y-10 divide-y divide-gray-200">
|
<div className="space-y-10 divide-y divide-gray-200">
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg leading-6 font-medium text-gray-900">Add new users</h3>
|
<h3 className="text-lg leading-6 font-medium text-gray-900">Add new users</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:p-6">
|
<div className="sm:px-6 pt-6">
|
||||||
<Tabs
|
<ProgressSteps steps={steps} onStepClick={handleStepClick}>
|
||||||
tabs={[
|
{getActiveStepIndex === 0 ? renderUsersCsvDataInput() : renderAppAccess()}
|
||||||
{ name: 'Add users', component: renderUsersCsvDataInput() },
|
</ProgressSteps>
|
||||||
{ name: 'App access roles', component: renderAppAccess() },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue