NTP-28762 | Bulk upload NDRA (#1339)
* NTP-28762 | Bulk upload NDRA * NTP-28762 | Bulk upload NDRA * NTP-28762 | Bulk upload NDRA * NTP-28762 | Bulk upload NDRA * NTP-28762 | Bulk NDRA Upload flow * NTP-28762 | Text change * NTP-28762 | Bulk NDRA Upload flow
This commit is contained in:
@@ -3,6 +3,7 @@ import { TabItem, Tabs } from '@navi/web-ui/lib/components';
|
||||
import {
|
||||
BULK_UPLOAD_DRAWER_TABS,
|
||||
CREATE_NEW,
|
||||
NDRA,
|
||||
ROLE_OPTIONS,
|
||||
UPDATE_EXISTING,
|
||||
UPLOAD_NUMBERS,
|
||||
@@ -19,6 +20,7 @@ import { SelectPickerOptionProps } from '@cp/src/components/interfaces';
|
||||
import { RootState } from '@cp/src/store';
|
||||
import { Roles } from '../../auth/constants/AuthConstants';
|
||||
import { TabsChild } from '@navi/web-ui/lib/components/Tabs/types';
|
||||
import CircularLoaderIcon from '../../Enach/Constants/CircularLoaderIconCopy';
|
||||
|
||||
type bulkUploaderDrawerProps = {
|
||||
showUploadDrawer: boolean;
|
||||
@@ -29,11 +31,14 @@ const BulkUploaderDrawer = ({ showUploadDrawer, setShowUploadDrawer }: bulkUploa
|
||||
const isCreateUserFeatureFlag = useSelector(
|
||||
(store: RootState) => store.common.featureFlags?.createUserFeatureFlag
|
||||
);
|
||||
const isNdraUploadFeatureFlag = useSelector(
|
||||
(store: RootState) => store.common.featureFlags?.ndraUploadAccess
|
||||
);
|
||||
|
||||
const user = useSelector((state: RootState) => state.common.userData);
|
||||
const isNumberManagementRole = user?.roles?.includes(Roles.ROLE_NUMBER_MANAGEMENT);
|
||||
|
||||
const [selectedTab, setSelectedTab] = useState(CREATE_NEW);
|
||||
const [selectedTab, setSelectedTab] = useState<string>();
|
||||
const [createSelectedRole, setCreateSelectedRole] = useState<
|
||||
SelectPickerOptionProps | undefined
|
||||
>();
|
||||
@@ -46,6 +51,7 @@ const BulkUploaderDrawer = ({ showUploadDrawer, setShowUploadDrawer }: bulkUploa
|
||||
const [createUploadFile, setCreateUploadFile] = useState<File | null>(null);
|
||||
const [editUploadFile, setEditUploadFile] = useState<File | null>(null);
|
||||
const [numberUploadFile, setNumberUploadFile] = useState<File | null>(null);
|
||||
const [ndraUploadFile, setNdraUploadFile] = useState<File | null>(null);
|
||||
|
||||
const handleCreateFileUpload = (file: File) => {
|
||||
setCreateUploadFile(file);
|
||||
@@ -62,18 +68,27 @@ const BulkUploaderDrawer = ({ showUploadDrawer, setShowUploadDrawer }: bulkUploa
|
||||
setUploadedFile(file);
|
||||
};
|
||||
|
||||
const handleNdraFileUpload = (file: File) => {
|
||||
setNdraUploadFile(file);
|
||||
setUploadedFile(file);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedTab) return;
|
||||
if (isCreateUserFeatureFlag) {
|
||||
setSelectedTab(CREATE_NEW);
|
||||
setUploadedFile(createUploadFile);
|
||||
} else if (isNumberManagementRole) {
|
||||
setSelectedTab(UPLOAD_NUMBERS);
|
||||
setUploadedFile(numberUploadFile);
|
||||
} else if (isNdraUploadFeatureFlag) {
|
||||
setSelectedTab(NDRA);
|
||||
setUploadedFile(ndraUploadFile);
|
||||
} else {
|
||||
setSelectedTab(UPDATE_EXISTING);
|
||||
setUploadedFile(editUploadFile);
|
||||
}
|
||||
}, [isCreateUserFeatureFlag]);
|
||||
}, [isCreateUserFeatureFlag, isNdraUploadFeatureFlag]);
|
||||
|
||||
const handleTabChange = (newTab: string) => {
|
||||
setSelectedTab(newTab);
|
||||
@@ -81,6 +96,8 @@ const BulkUploaderDrawer = ({ showUploadDrawer, setShowUploadDrawer }: bulkUploa
|
||||
setUploadedFile(createUploadFile);
|
||||
} else if (newTab === UPDATE_EXISTING) {
|
||||
setUploadedFile(editUploadFile);
|
||||
} else if (newTab === NDRA) {
|
||||
setUploadedFile(ndraUploadFile);
|
||||
} else {
|
||||
setUploadedFile(numberUploadFile);
|
||||
}
|
||||
@@ -112,6 +129,37 @@ const BulkUploaderDrawer = ({ showUploadDrawer, setShowUploadDrawer }: bulkUploa
|
||||
setCreateUploadFile(null);
|
||||
setIsUploadDisabled(false);
|
||||
});
|
||||
} else if (selectedTab === NDRA && ndraUploadFile) {
|
||||
const url = getApiUrl(ApiKeys.UPLOAD_NDRA);
|
||||
const formData = new FormData();
|
||||
formData.append('file', ndraUploadFile);
|
||||
axiosInstance
|
||||
.post(url, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
donotHandleError: true
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
toast.success('NDRA upload successful');
|
||||
setShowUploadDrawer(false);
|
||||
})
|
||||
.catch(error => {
|
||||
const errorMessage = error?.response?.data?.message;
|
||||
toast.error(
|
||||
errorMessage || (
|
||||
<div>
|
||||
Upload failed! Check upload history <br />
|
||||
for more info
|
||||
</div>
|
||||
)
|
||||
);
|
||||
setShowUploadDrawer(false);
|
||||
})
|
||||
.finally(() => {
|
||||
setNdraUploadFile(null);
|
||||
setIsUploadDisabled(false);
|
||||
});
|
||||
} else {
|
||||
if (editUploadFile) {
|
||||
const url = getApiUrl(ApiKeys.UPLOAD_EDIT_AGENTS);
|
||||
@@ -179,6 +227,10 @@ const BulkUploaderDrawer = ({ showUploadDrawer, setShowUploadDrawer }: bulkUploa
|
||||
return createActionType ? !validationOfFile(numberUploadFile) : true;
|
||||
}
|
||||
|
||||
if (selectedTab === NDRA) {
|
||||
return !validationOfFile(ndraUploadFile);
|
||||
}
|
||||
|
||||
if (selectedTab === CREATE_NEW) {
|
||||
return createSelectedRole ? !validationOfFile(createUploadFile) : true;
|
||||
}
|
||||
@@ -191,6 +243,8 @@ const BulkUploaderDrawer = ({ showUploadDrawer, setShowUploadDrawer }: bulkUploa
|
||||
return handleNumberFileUpload;
|
||||
} else if (selectedTab === CREATE_NEW) {
|
||||
return handleCreateFileUpload;
|
||||
} else if (selectedTab === NDRA) {
|
||||
return handleNdraFileUpload;
|
||||
} else {
|
||||
return handleEditFileUpload;
|
||||
}
|
||||
@@ -201,6 +255,8 @@ const BulkUploaderDrawer = ({ showUploadDrawer, setShowUploadDrawer }: bulkUploa
|
||||
return numberUploadFile;
|
||||
} else if (selectedTab === CREATE_NEW) {
|
||||
return createUploadFile;
|
||||
} else if (selectedTab === NDRA) {
|
||||
return ndraUploadFile;
|
||||
} else {
|
||||
return editUploadFile;
|
||||
}
|
||||
@@ -211,6 +267,8 @@ const BulkUploaderDrawer = ({ showUploadDrawer, setShowUploadDrawer }: bulkUploa
|
||||
return setNumberUploadFile;
|
||||
} else if (selectedTab === CREATE_NEW) {
|
||||
return setCreateUploadFile;
|
||||
} else if (selectedTab === NDRA) {
|
||||
return setNdraUploadFile;
|
||||
} else {
|
||||
return setEditUploadFile;
|
||||
}
|
||||
@@ -221,6 +279,8 @@ const BulkUploaderDrawer = ({ showUploadDrawer, setShowUploadDrawer }: bulkUploa
|
||||
return CREATE_NEW;
|
||||
} else if (isNumberManagementRole) {
|
||||
return UPLOAD_NUMBERS;
|
||||
} else if (isNdraUploadFeatureFlag) {
|
||||
return NDRA;
|
||||
} else {
|
||||
return UPDATE_EXISTING;
|
||||
}
|
||||
@@ -261,6 +321,9 @@ const BulkUploaderDrawer = ({ showUploadDrawer, setShowUploadDrawer }: bulkUploa
|
||||
if (item.key === UPLOAD_NUMBERS && !isNumberManagementRole) {
|
||||
return acc;
|
||||
}
|
||||
if (item.key === NDRA && !isNdraUploadFeatureFlag) {
|
||||
return acc;
|
||||
}
|
||||
{
|
||||
acc.push(
|
||||
<TabItem key={item.key} label={item.value}>
|
||||
@@ -309,7 +372,10 @@ const BulkUploaderDrawer = ({ showUploadDrawer, setShowUploadDrawer }: bulkUploa
|
||||
</>
|
||||
) : null}
|
||||
<Typography variant="p5" className="text-grayscale-2 pb-1">
|
||||
Upload a CSV to update user details
|
||||
Upload a CSV to{' '}
|
||||
{selectedTab === NDRA
|
||||
? 'generate NDRA certificates'
|
||||
: 'update user details'}
|
||||
</Typography>
|
||||
<FileUploader
|
||||
key={item.key}
|
||||
@@ -318,6 +384,14 @@ const BulkUploaderDrawer = ({ showUploadDrawer, setShowUploadDrawer }: bulkUploa
|
||||
uploadedFile={getUploadedFileHandler()}
|
||||
setUploadedFile={setUploadedFileHandler()}
|
||||
/>
|
||||
{isUploadDisabled ? (
|
||||
<div className="flex items-center gap-1 mt-4">
|
||||
<CircularLoaderIcon fill="var(--navi-color-gray-c3)" />
|
||||
<Typography variant="h5" color="var(--navi-color-gray-c3)">
|
||||
Validating...
|
||||
</Typography>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</TabItem>
|
||||
|
||||
@@ -9,6 +9,7 @@ import ErrorIcon from '@cp/src/assets/icons/ErrorIcon';
|
||||
import {
|
||||
BULK_UPLOAD_FLOW,
|
||||
CREATE_NEW,
|
||||
NDRA,
|
||||
ROLE_OPTIONS,
|
||||
UPLOAD_NUMBERS,
|
||||
validationOfFile
|
||||
@@ -31,13 +32,14 @@ const FileUploader = ({
|
||||
const hiddenFileInput = useRef<HTMLInputElement>(null);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [hasError, setHasError] = useState(false);
|
||||
const isSelectedTabCreateNew = selectedTab === CREATE_NEW;
|
||||
|
||||
const handleDownloadClick = () => {
|
||||
if (selectedTab === CREATE_NEW) {
|
||||
dispatch(downloadSampleFile(BULK_UPLOAD_FLOW.CREATE));
|
||||
} else if (selectedTab === UPLOAD_NUMBERS) {
|
||||
dispatch(downloadSampleFile(BULK_UPLOAD_FLOW.NUMBER));
|
||||
} else if (selectedTab === NDRA) {
|
||||
dispatch(downloadSampleFile(BULK_UPLOAD_FLOW.NDRA));
|
||||
} else {
|
||||
dispatch(downloadSampleFile(BULK_UPLOAD_FLOW.EDIT));
|
||||
}
|
||||
|
||||
@@ -6,10 +6,16 @@ import LoaderIcon from '@cp/src/assets/icons/LoaderIcon';
|
||||
import ErrorIconOutline from '@cp/src/assets/icons/ErrorIconOutline';
|
||||
import { useMemo } from 'react';
|
||||
import { FILE_STATUS } from '../../AmeyoUtility/types';
|
||||
import { fetchFailureReportFile, fetchFile } from './actions/uploadHistoryDrawerActions';
|
||||
import {
|
||||
fetchFailureReportFile,
|
||||
fetchFile,
|
||||
fetchNdraFailureFile
|
||||
} from './actions/uploadHistoryDrawerActions';
|
||||
import dayjs from 'dayjs';
|
||||
import { DateTimeFormat } from '@cp/src/components/DateTimePicker/constants';
|
||||
import { errorActionType, successActionType } from './types.ts/types';
|
||||
import { BulkUploadFileType, errorActionType, successActionType } from './types.ts/types';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@cp/src/components/TooltipV2/TooltipV2';
|
||||
import cx from 'classnames';
|
||||
|
||||
export const useColumnDefs = (): {
|
||||
columnDefs: ColDef[];
|
||||
@@ -20,6 +26,15 @@ export const useColumnDefs = (): {
|
||||
fetchFile(fileReferenceId);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFailureReportButtonClick = (fileReferenceId: string, type: string) => {
|
||||
if (type === BulkUploadFileType.UPLOAD_NDRA_CERTIFICATE) {
|
||||
fetchNdraFailureFile(fileReferenceId);
|
||||
return;
|
||||
}
|
||||
fetchFailureReportFile(fileReferenceId);
|
||||
};
|
||||
|
||||
const columnDefs: ColDef[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
@@ -150,11 +165,25 @@ export const useColumnDefs = (): {
|
||||
onClick={() => handleFileButtonClick(data?.referenceId, data?.status)}
|
||||
startAdornment={data?.status && icon}
|
||||
>
|
||||
<span className={styles.buttonText}>
|
||||
{data?.status && (data?.status === 'COMPLETED' || data?.status === 'FAILED')
|
||||
? 'Download'
|
||||
: 'In Progress'}
|
||||
</span>
|
||||
<Tooltip placement="top" hideStrategy="referenceHidden">
|
||||
<TooltipTrigger>
|
||||
<div
|
||||
className={cx(
|
||||
styles.buttonText,
|
||||
data?.originalFileName ? 'truncate block w-[80px]' : ''
|
||||
)}
|
||||
>
|
||||
{data?.status && (data?.status === 'COMPLETED' || data?.status === 'FAILED')
|
||||
? `${data?.originalFileName || 'Download'}`
|
||||
: `${data?.originalFileName || 'In Progress'}`}
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
{data?.originalFileName && (
|
||||
<TooltipContent className={'tooltipWrapper'}>
|
||||
{data?.originalFileName}
|
||||
</TooltipContent>
|
||||
)}
|
||||
</Tooltip>
|
||||
</Button>
|
||||
) : (
|
||||
<Typography variant="p4" color="var(--grayscale-2)">
|
||||
@@ -176,7 +205,7 @@ export const useColumnDefs = (): {
|
||||
<Button
|
||||
variant="text"
|
||||
className={styles.buttonWrapper}
|
||||
onClick={() => fetchFailureReportFile(data?.referenceId)}
|
||||
onClick={() => handleFailureReportButtonClick(data?.referenceId, data?.type)}
|
||||
startAdornment={<Download />}
|
||||
>
|
||||
<span className={styles.buttonText}>Download</span>
|
||||
|
||||
@@ -100,6 +100,7 @@
|
||||
.ag-full-width-row .ag-cell-wrapper.ag-row-group {
|
||||
padding-left: 19px;
|
||||
padding-right: 4px;
|
||||
border: 0px;
|
||||
}
|
||||
.ag-header-cell-text {
|
||||
width: auto;
|
||||
|
||||
@@ -53,3 +53,11 @@ export const fetchFile = (fileReferenceId: string) => {
|
||||
getFileDownload(fileUrl);
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchNdraFailureFile = (fileReferenceId: string) => {
|
||||
const url = getApiUrl(ApiKeys.DOWNLOAD_NDRA_FAILURE_FILE, { referenceId: fileReferenceId });
|
||||
return axiosInstance.get(url).then(response => {
|
||||
const fileUrl = response?.data;
|
||||
getFileDownload(fileUrl);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2,16 +2,22 @@ export const successActionType: { [key: string]: string } = {
|
||||
UPLOAD_USERS: 'Created new agents',
|
||||
UPLOAD_EDIT_USER: 'Edited bulk agents',
|
||||
UPLOAD_USER_NUMBER_MAPPING_ADD: 'Adding numbers',
|
||||
UPLOAD_USER_NUMBER_MAPPING_DELETE: 'Deleting numbers'
|
||||
UPLOAD_USER_NUMBER_MAPPING_DELETE: 'Deleting numbers',
|
||||
UPLOAD_NDRA_CERTIFICATE: 'Uploaded NDRA'
|
||||
};
|
||||
|
||||
export const errorActionType: { [key: string]: string } = {
|
||||
UPLOAD_USERS: 'Failed to create',
|
||||
UPLOAD_EDIT_USER: 'Failed to edit',
|
||||
UPLOAD_USER_NUMBER_MAPPING_ADD: 'Failed to add numbers',
|
||||
UPLOAD_USER_NUMBER_MAPPING_DELETE: 'Failed to delete numbers'
|
||||
UPLOAD_USER_NUMBER_MAPPING_DELETE: 'Failed to delete numbers',
|
||||
UPLOAD_NDRA_CERTIFICATE: 'Failed to upload NDRA'
|
||||
};
|
||||
|
||||
export enum BulkUploadFileType {
|
||||
UPLOAD_NDRA_CERTIFICATE = 'UPLOAD_NDRA_CERTIFICATE'
|
||||
}
|
||||
|
||||
export const originalFileStatus: { [key: string]: string } = {
|
||||
INITIATED: 'In progress',
|
||||
IN_PROGRESS: 'In progress',
|
||||
|
||||
@@ -336,7 +336,8 @@ const AllAgents = () => {
|
||||
startTime: item.startTime,
|
||||
owner: item.owner,
|
||||
status: item.status,
|
||||
referenceId: item.referenceId
|
||||
referenceId: item.referenceId,
|
||||
originalFileName: item.originalFileName
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
@@ -85,15 +85,19 @@ export interface GridEvent {
|
||||
export const BULK_UPLOAD_DRAWER_TABS = [
|
||||
{
|
||||
key: 'createNew',
|
||||
value: 'Create New User'
|
||||
value: 'New users'
|
||||
},
|
||||
{
|
||||
key: 'updateExisting',
|
||||
value: 'Update Existing Users'
|
||||
value: 'Existing users'
|
||||
},
|
||||
{
|
||||
key: 'uploadNumbers',
|
||||
value: 'Update Phone Numbers'
|
||||
value: 'Phone numbers'
|
||||
},
|
||||
{
|
||||
key: 'uploadNdra',
|
||||
value: 'NDRA'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -126,12 +130,14 @@ export const UPLOAD_OPTIONS = [
|
||||
export enum BULK_UPLOAD_FLOW {
|
||||
CREATE = 'create',
|
||||
EDIT = 'edit',
|
||||
NUMBER = 'user_number_mapping'
|
||||
NUMBER = 'user_number_mapping',
|
||||
NDRA = 'ndra'
|
||||
}
|
||||
|
||||
export const CREATE_NEW = 'createNew';
|
||||
export const UPDATE_EXISTING = 'updateExisting';
|
||||
export const UPLOAD_NUMBERS = 'uploadNumbers';
|
||||
export const NDRA = 'uploadNdra';
|
||||
|
||||
export const uploadAgentsSampleCsvFile =
|
||||
'phone_number,email_address,name,active,reporting_manager_email_address,state,employee_id,primary_language,secondary_language,tertiary_language,bucket_group,agent_center\n' +
|
||||
|
||||
@@ -303,7 +303,9 @@ export enum ApiKeys {
|
||||
UPDATE_ANOMALY_TICKET,
|
||||
RESOLVE_ANOMALY_TICKET,
|
||||
GET_ANOMALY_RCA_QUESTION,
|
||||
GET_ANOMALY_ACTIVITY_LOG
|
||||
GET_ANOMALY_ACTIVITY_LOG,
|
||||
UPLOAD_NDRA,
|
||||
DOWNLOAD_NDRA_FAILURE_FILE
|
||||
}
|
||||
|
||||
// TODO: try to get rid of `as`
|
||||
@@ -608,6 +610,8 @@ API_URLS[ApiKeys.GET_ANOMALY_RCA_QUESTION] =
|
||||
'/longhorn/anomaly-tracker/question-tree/rca/{anomalyReferenceId}';
|
||||
API_URLS[ApiKeys.GET_ANOMALY_ACTIVITY_LOG] =
|
||||
'/longhorn/anomaly-tracker/activity-logs/{anomalyReferenceId}';
|
||||
API_URLS[ApiKeys.UPLOAD_NDRA] = '/agent/bulk/ndra';
|
||||
API_URLS[ApiKeys.DOWNLOAD_NDRA_FAILURE_FILE] = '/uploads/v2/{referenceId}/failure-report';
|
||||
|
||||
// TODO: try to get rid of `as`
|
||||
const MOCK_API_URLS: Record<ApiKeys, string> = {} as Record<ApiKeys, string>;
|
||||
|
||||
Reference in New Issue
Block a user