Access Number Management - Copy this React, Tailwind Component to your project
import React, { useEffect, useState } from "react"; import * as BS from "react-bootstrap"; import $ from "jquery"; import "react-dual-listbox/lib/react-dual-listbox.css"; import NewIcon from "../../../images/new.png"; import EditIcon from "../../../images/Edit.png"; import DeleteIcon from "../../../images/Delete.png"; import * as SPSUtils from "../../../utils/SPSUtils.js"; import * as SPSConstants from "../../../utils/SPSConstants"; import SearchCmpt from "../../searchcmpt/SearchMgmt"; import NewPagination from "../../pagination/Pagination"; import { I18nProvider } from "../../../i18n"; import Translator from "../../../i18n/Translator"; import DraggableModalDialog from "../../DraggableModalDialog"; const pageSize = 50; // Default page size const searchFields = [ { label: "Access Number Id", value: "ACCESS_NUMBER_SEQ" }, { label: "Description", value: "DESCRIPTION" }, { label: "Call Flow Name", value: "CALL_FLOW_NAME" }, { label: "Country", value: "COUNTRY_NAME" }, ]; const defaultAccessMgmtNumber = { access_NUMBER_SEQ: "", description: "", phoneNumber: "", call_FLOW_NAME: "", allocated_SERVICE_ID: "", country_NAME: "", type: "", status: "Active", assignedTo: "", }; const AccessMgmtNumberScreen = (props) => { const [param] = useState(props.input); const [selectedProvAccDetail] = useState(props.selectedProvAccDetail); const [records, setRecords] = useState([]); const [totalRecords, setTotalRecords] = useState(0); const [currentPage, setCurrentPage] = useState(1); const [addRecord, setAddRecord] = useState(false); const [errorAlert, setErrorAlert] = useState(false); const [errorMsg, setErrorMsg] = useState(""); const [selectedItem, setSelectedItem] = useState({ ...defaultAccessMgmtNumber }); const [entityList, setEntityList] = useState([]); const [requestForDelete, setRequestForDelete] = useState(false); const [isReadOnlyAccess] = useState(SPSUtils.isReadOnlyAccess(props.input)); const [assignedToOptions, setAssignedToOptions] = useState([]); const [orgName, setOrgName] = useState(""); const [errors, setErrors] = useState({}); const [multiSearchOptions, setMultiSearchOptions] = useState([]); // Kept in case usage is added later const [pageInfo, setPageInfo] = useState({ offset: 0, limit: pageSize, total: -1 }); // Define pageInfo useEffect(() => { loadPhoneInfo(); fetchEntityList(); fetchAssignedToOptions(); }, [selectedProvAccDetail.selectedProviderAccId]); const loadPhoneInfo = () => { const filter = { conditions: [ SPSUtils.getFilterCondition("accountNumber", SPSConstants.EQUALS_OPERATOR, selectedProvAccDetail.selectedProviderAccId), ], }; $.ajax({ method: "GET", url: SPSConstants.SPS_BASE_URL + `api/common/account/${selectedProvAccDetail.selectedProviderAccId}/phone/list?filter=` + encodeURIComponent(JSON.stringify(filter)), dataType: "json", beforeSend: (xhr) => { xhr.setRequestHeader("Authorization", "Bearer " + param.token); }, }) .done((result) => { const records = result.phoneInfos || []; if (Array.isArray(records) && records.length > 0) { getAccessMgmtNumberScreen(records); } else { setRecords([]); } }) .fail((xhr) => { let message = xhr.statusText; if (xhr.responseJSON && xhr.responseJSON.uiErrorMessages) { message = xhr.responseJSON.uiErrorMessages[0]; } setErrorAlert(true); setErrorMsg(message); }); }; const getAccessMgmtNumberScreen = (records) => { const phoneMapping = {}; records.forEach(phoneInfo => { if (phoneInfo.phoneNumber && phoneInfo.phoneSeq) { phoneMapping[phoneInfo.phoneSeq] = phoneInfo.phoneNumber; } }); const conditions = [ { field: 'accountNumber', operation: 'EQUALS', value: selectedProvAccDetail.selectedProviderAccId, }, ]; const filter = { combiningOperator: 'AND', pageInfo: { ...pageInfo, total: -1 }, conditions, }; $.ajax({ method: "GET", url: SPSConstants.SPS_BASE_URL + `api/prepaidtelmexproject/9/account/${selectedProvAccDetail.selectedProviderAccId}/common_access_number_account/list?filter=` + encodeURIComponent(JSON.stringify(filter)), dataType: "json", beforeSend: (xhr) => { xhr.setRequestHeader("Authorization", "Bearer " + param.token); }, }) .done((result) => { const totalRecords = result.pageInfo?.total || 0; let accessRecords = (result.records || []).map(accessRecord => ({ ...accessRecord, phoneNumber: phoneMapping[accessRecord.access_NUMBER_SEQ] || null, })); setRecords(accessRecords); setTotalRecords(totalRecords); setPageInfo(result.pageInfo); }) .fail((xhr) => { let message = xhr.statusText; if (xhr.responseJSON && xhr.responseJSON.uiErrorMessages) { message = xhr.responseJSON.uiErrorMessages[0]; } setErrorAlert(true); setErrorMsg(message); }); }; const fetchAssignedToOptions = () => { const filter = { conditions: [ { field: 'accountType', operation: 'NOT_EQUALS', value: 'n' }, { field: 'accountType', operation: 'NOT_EQUALS', value: 's' }, ], }; $.ajax({ method: "GET", url: SPSConstants.SPS_BASE_URL + 'api/common/orgaccount/list?filter=' + encodeURIComponent(JSON.stringify(filter)), dataType: "json", beforeSend: (xhr) => { xhr.setRequestHeader("Authorization", "Bearer " + param.token); }, }) .done((result) => { const options = (result.orgAccounts || []).map(record => ({ accountNumber: record.accountNumber, orgName: record.orgName, displayName: `${record.accountNumber} - ${record.orgName}`, })); setAssignedToOptions(options); if (selectedProvAccDetail.selectedProviderAccId) { const matchedAccount = options.find(opt => opt.accountNumber === selectedProvAccDetail.selectedProviderAccId); if (matchedAccount) { setOrgName(matchedAccount.orgName); } } }) .fail((xhr) => { let message = xhr.statusText; if (xhr.responseJSON && xhr.responseJSON.uiErrorMessages) { message = xhr.responseJSON.uiErrorMessages[0]; } setErrorAlert(true); setErrorMsg(message); }); }; const fetchEntityList = () => { const accountNumber = selectedProvAccDetail.selectedProviderAccId; // Use the state variable directly const conditions = [ SPSUtils.getFilterCondition("accountNumber", SPSConstants.EQUALS_OPERATOR, accountNumber) ]; const filter = { conditions: conditions }; // Log filter and prepared URL for debugging console.log("Filter object:", JSON.stringify(filter)); const requestUrl = SPSConstants.SPS_BASE_URL + `api/prepaidtelmexproject/9/entitymaster/list?filter=` + encodeURIComponent(JSON.stringify(filter)); console.log("Request URL:", requestUrl); $.ajax({ method: "GET", url: requestUrl, dataType: "json", beforeSend: (xhr) => { xhr.setRequestHeader("Authorization", "Bearer " + param.token); // No need for this.state }, }) .done((result) => { const entityList = result.records || []; setEntityList(entityList); // Directly use state setter }) .fail((xhr) => { let message = xhr.statusText; if (xhr.responseJSON && xhr.responseJSON.uiErrorMessages) { message = xhr.responseJSON.uiErrorMessages[0]; } setErrorAlert(true); setErrorMsg(message); }); }; const addAccessMgmtNumber = () => { const assignedToOption = assignedToOptions[0]; setAddRecord(true); setSelectedItem({ ...defaultAccessMgmtNumber, assignedTo: assignedToOption ? assignedToOption.orgName : "", }); }; const closeAddModal = () => { setAddRecord(false); setErrors({}); setSelectedItem({ ...defaultAccessMgmtNumber }); }; const onChangeCB = (event) => { const { name, value } = event.target; const newSelectedItem = { ...selectedItem, [name]: value }; setSelectedItem(newSelectedItem); const newErrors = { ...errors }; if (name === "phoneNumber") { if (value.length > 10) { newErrors.phoneNumber = "Length of phone Number should be equal to 10"; } else if (!/^[0-9]*$/.test(value)) { newErrors.phoneNumber = "Only Numeric Values allowed"; } else { newErrors.phoneNumber = ""; } } setErrors(newErrors); }; const _saveLcbData = () => { const { phoneNumber } = selectedItem; const newErrors = {}; if (!phoneNumber) { newErrors.phoneNumber = "Required."; } else { const numericRegex = /^[0-9]*$/; if (phoneNumber.length > 10) { newErrors.phoneNumber = "Length of phone Number should be equal to 10"; } else if (!numericRegex.test(phoneNumber)) { newErrors.phoneNumber = "Only Numeric Values allowed"; } } if (Object.keys(newErrors).length > 0) { setErrors(newErrors); return; } saveDataInPhoneInfoAndCommonAccess(); }; const saveDataInPhoneInfoAndCommonAccess = () => { const params = createSaveParams(); compositeReq(params) .then(() => { closeAddEditDialog(); loadPhoneInfo(); }) .catch((errorMessage) => { setErrorAlert(true); setErrorMsg(errorMessage); }); }; const createSaveParams = () => { let params = {}; params.contextVariable = "response"; let reqs = []; let req1 = { api: "phoneInfoAllowed", operation: "CREATE", data: setPhoneInfoParams(), }; reqs.push(req1); let req2 = { api: "COMMON_ACCESS_NUMBER_ACCOUNTAllowed", operation: "CREATE", data: setCommonAccessNumberAccount(), }; reqs.push(req2); params.requests = reqs; return params; }; const compositeReq = (params) => { return new Promise((resolve, reject) => { $.ajax({ method: "POST", url: SPSConstants.SPS_BASE_URL + "api/common/composite", data: JSON.stringify(params), dataType: "json", beforeSend: (xhr) => { SPSUtils.postReqHeader(xhr); }, }) .done((result) => { resolve(result); }) .fail((xhr) => { let errorMessage = 'An unexpected error occurred'; if (xhr.responseJSON && xhr.responseJSON.uiErrorMessages) { errorMessage = xhr.responseJSON.uiErrorMessages[0]; } reject(errorMessage); }); }); }; const setPhoneInfoParams = () => { const inParams = { ...selectedItem }; return { phoneNumber: inParams.phoneNumber, phoneTag: "ACCESS NUMBER", accountNumber: selectedProvAccDetail.selectedProviderAccId, switchId: "", phoneSeq: inParams.access_NUMBER_SEQ, }; }; const setCommonAccessNumberAccount = () => { const inParams = { ...selectedItem }; const selectedEntity = entityList.find(entity => entity.entity_NAME === inParams.country_NAME); const entityCode = selectedEntity ? selectedEntity.entity_CODE : null; return { access_NUMBER_SEQ: "{!response.results[0].phoneSeq!}", description: inParams.description, accountNumber: selectedProvAccDetail.selectedProviderAccId, call_FLOW_NAME: inParams.call_FLOW_NAME, allocated_SERVICE_ID: selectedProvAccDetail.selectedProviderAccId, country_NAME: inParams.country_NAME, origin_COUNTRY_CODE: entityCode, type: mapType(inParams.type), status: inParams.status === "Active" ? "1" : "0", }; }; const mapType = (type) => { switch (type) { case "MAPPED": return "M"; case "TOLL FREE": return "T"; case "DIRECT DIAL": return "D"; default: return "C"; } }; const closeAddEditDialog = () => { setAddRecord(false); setErrorAlert(false); setErrorMsg(''); }; const editLcb = (item) => { // Implement your edit logic here setSelectedItem(item); setAddRecord(true); }; const getStartEndValue = () => { let start = (currentPage - 1) * pageSize + 1; let end = currentPage * pageSize; if (end > totalRecords) { end = totalRecords; } return { start, end }; }; const { start, end } = getStartEndValue(); const deleteRecord = () => { // Implement your delete logic here console.log("Record deleted"); // Replace with actual delete logic setRequestForDelete(false); loadPhoneInfo(); }; const getDetailView = () => { return ( <div className="container SPSContainerHolder SuperRoutePlanGroupForm"> {/* Access Number Field */} <div className="row"> <div className="form-group col-3"> <label htmlFor="phoneNumber">Access Number</label> <span className="required">*</span> </div> <div className="form-group col-4"> <input disabled={selectedItem.access_NUMBER_SEQ} type="text" className="form-control" id="phoneNumber" name="phoneNumber" value={selectedItem.phoneNumber || ""} onChange={onChangeCB} /> {errors.phoneNumber && <div className='error-msg'>{errors.phoneNumber}</div>} </div> </div> {/* Description Field */} <div className="row"> <div className="form-group col-3"> <label htmlFor="description">Description</label> </div> <div className="form-group col-4"> <input type="text" className="form-control" id="description" name="description" value={selectedItem.description || ""} onChange={onChangeCB} /> {errors.description && <div className='error-msg'>{errors.description}</div>} </div> </div> {/* Call Flow Name Field */} <div className="row"> <div className="form-group col-3"> <label htmlFor="call_FLOW_NAME">Call Flow Name</label> </div> <div className="form-group col-4"> <input type="text" className="form-control" id="call_FLOW_NAME" name="call_FLOW_NAME" value={selectedItem.call_FLOW_NAME || ""} onChange={onChangeCB} /> </div> </div> {/* Type Field */} <div className="row"> <div className="form-group col-3"> <label htmlFor="type">Type</label> </div> <div className="form-group col-4"> <select className="form-control" id="type" name="type" value={selectedItem.type || ""} onChange={onChangeCB} > <option value="">Select Type</option> <option value="MAPPED">MAPPED</option> <option value="TOLL FREE">TOLL FREE</option> <option value="DIRECT DIAL">DIRECT DIAL</option> <option value="CS">CS</option> </select> </div> </div> {/* Status Field */} <div className="row"> <div className="form-group col-3"> <label htmlFor="status">Status</label> <span className="required">*</span> </div> <div className="form-group col-4"> <select className="form-control" id="status" name="status" value={selectedItem.status || "Active"} onChange={onChangeCB} > <option value="Active">Active</option> <option value="Inactive">Inactive</option> </select> </div> </div> {/* Country Field */} <div className="row"> <div className="form-group col-3"> <label htmlFor="country_NAME">Country</label> </div> <div className="form-group col-4"> <select className="form-control" name="country_NAME" value={selectedItem.country_NAME || ""} onChange={onChangeCB} > <option value="">Select Country</option> {entityList.map(entity => ( <option key={entity.entity_CODE} value={entity.entity_NAME}> {entity.entity_NAME} </option> ))} </select> </div> </div> {/* Assigned To Field */} <div className="row"> <div className="form-group col-3"> <label htmlFor="assignedTo">Assigned To</label> </div> <div className="form-group col-4"> <select className="form-control" id="assignedTo" name="assignedTo" value={selectedItem.assignedTo || ""} onChange={onChangeCB} > <option value="">Select User</option> {assignedToOptions.map(option => ( <option key={option.accountNumber} value={option.orgName}> {option.displayName} </option> ))} </select> </div> </div> </div> ); }; return ( <I18nProvider locale={param.languageselected}> {addRecord ? ( <div> <img src={NewIcon} className="NewIcon" onClick={addAccessMgmtNumber} data-toggle="tooltip" data-placement="top" title="Add" alt="Add New Access Number" /> </div> ) : ( <div className="SuperRoutePlanGroupCmpt"> <div className={props.searchPanelClass}> {props.searchPanelClass === 'ShowSearchBarPanel' && ( <SearchCmpt name="Super Route Plan Group Search" input={param} searchFieldMap={searchFields} fieldPatternType="{PATTERN_TYPE}" searchCmptRef={null} // This is likely the problem /> )} </div> <div className="ErrorDisplayPanel"> {errorAlert && ( <div className="row"> <div className="col"> <div className="alert alert-danger alert-dismissible fade show errorAlert" role="alert"> <button type="button" className="close" data-dismiss="alert" aria-label="Close" onClick={() => setErrorAlert(false)} > <span aria-hidden="true">×</span> </button> {Translator("time_error")} {errorMsg} </div> </div> </div> )} </div> <div className="SPSComponentTableHolder" style={{ marginTop: "2%" }}> <BS.Table responsive className="SPSComponentTable table-striped"> <thead className="SPSComponentTblHeader"> <tr> <th>Access Number Id</th> <th>Access Number</th> <th>Description</th> <th>Call Flow Name</th> <th>Service Provider Name</th> <th>Country</th> <th style={{ width: "10rem" }}>{Translator("global_action")}</th> </tr> </thead> <tbody className="SPSComponentTblBody AccountMgmtTblBody"> {records.map((item) => ( <tr key={item.access_NUMBER_SEQ}> <td>{item.access_NUMBER_SEQ}</td> <td>{item.phoneNumber}</td> <td>{item.description}</td> <td>{item.call_FLOW_NAME}</td> <td>{orgName}</td> <td>{item.country_NAME}</td> <td style={{ width: "10rem" }}> <div className="row ActionIcons"> <button className="col-2 btn btn-default btn-transparent" onClick={() => editLcb(item)} // Use the editLcb function added earlier data-toggle="tooltip" data-placement="top" title="Edit" alt="Edit Access Number" > <img src={EditIcon} className="EditIcon" alt="Edit Icon" /> </button> <button className="col-2 btn btn-default btn-transparent" onClick={() => setRequestForDelete(true)} // Trigger deletion confirmation data-toggle="tooltip" data-placement="top" title="Delete" alt="Delete Access Number" > <img src={DeleteIcon} className="DeleteIcon" alt="Delete Icon" /> </button> </div> </td> </tr> ))} </tbody> </BS.Table> </div> <div className="paginationNew"> <div className="recordCount"> {totalRecords > 0 ? ( <h2 className={"text-dark"}> {totalRecords > pageSize ? ( <> <strong className="text-secondary">Page</strong>{" "} <select className="paginationSelect" value={currentPage} onChange={(e) => { setCurrentPage(parseInt(e.target.value)); }} > {Array.from({ length: Math.ceil(totalRecords / pageSize) }, (_, i) => ( <option key={i} value={i + 1}> {i + 1} </option> ))} </select>{" "} |{" "} </> ) : null} <strong className="text-secondary"> Viewing {start} - {end} of {totalRecords} </strong>{" "} </h2> ) : ( SPSUtils.NO_RECORDS_FOUND )} </div> <div className="paginationComp"> <NewPagination totalRecords={totalRecords} pageLimit={pageSize} pageNeighbours={1} onPageChanged={(data) => { setCurrentPage(data.currentPage); loadPhoneInfo(); // Load new data for the selected page }} current_page={currentPage} /> <div> <img src={NewIcon} className='pageIcon' onClick={addAccessMgmtNumber} alt="Add New Access Number" /> </div> </div> </div> </div> )} <BS.Modal dialogAs={DraggableModalDialog} dialogClassName="modal-dialog-centered lcbModal" show={addRecord} onHide={closeAddModal}> <BS.Modal.Header closeButton> <BS.Modal.Title> <strong>Access Number Management</strong> </BS.Modal.Title> </BS.Modal.Header> <BS.Modal.Body> {errorAlert && ( <div className="row"> <div className="col"> <div className="alert alert-danger" role="alert"> <button type="button" className="close" data-dismiss="alert" aria-label="Close" onClick={() => setErrorAlert(false)} > <span aria-hidden="true">×</span> </button> {errorMsg} </div> </div> </div> )} {getDetailView()} </BS.Modal.Body> <BS.Modal.Footer> <button className="btn SaveBtn" onClick={closeAddModal}> {Translator("global_cancel")} </button> <button className="btn SaveBtn" onClick={_saveLcbData} disabled={isReadOnlyAccess}> {Translator("global_save")} </button> </BS.Modal.Footer> </BS.Modal> <BS.Modal dialogAs={DraggableModalDialog} dialogClassName="modal-dialog-centered DeleteRequestModal" show={requestForDelete} onHide={() => setRequestForDelete(false)}> <BS.Modal.Body> {errorAlert && ( <div className="row"> <div className="col"> <div className="alert alert-danger " role="alert"> <button type="button" className="close" data-dismiss="alert" aria-label="Close" onClick={() => setErrorAlert(false)} > <span aria-hidden="true">×</span> </button> {errorMsg} </div> </div> </div> )} <div className="ConfirmationMessageHolder"> <p>{Translator("global_deleteconfirmation")}</p> </div> </BS.Modal.Body> <BS.Modal.Footer> <button className="btn DeleteDialogBtn" onClick={() => setRequestForDelete(false)}> {Translator("global_cancel")} </button> <button className="btn DeleteDialogBtn" onClick={deleteRecord}> {Translator("global_ok")} </button> </BS.Modal.Footer> </BS.Modal> </I18nProvider> ); }; export default AccessMgmtNumberScreen;
