From 3d0c1fbfcb877a15d9d4662b4b79bff7be15213e Mon Sep 17 00:00:00 2001 From: chaitanyakole Date: Tue, 24 Dec 2024 13:55:02 +0530 Subject: [PATCH] Tenant,cohort- admin creation , Added filters of tenant and cohort, correction of listing --- public/locales/en/common.json | 16 +- src/components/ActionIcon.tsx | 245 ++++++------- src/components/AddNewTenant.tsx | 26 +- src/components/AreaSelection.tsx | 43 ++- src/components/FormControl.tsx | 34 +- src/components/HeaderComponent.tsx | 3 +- src/components/KaTableComponent.tsx | 39 +++ src/components/TenantSchema.json | 16 +- src/components/UserTable.tsx | 146 ++++++-- src/data/tableColumns.ts | 14 +- src/pages/cohortAdminSchema.json | 54 +++ src/pages/cohortSchema.json | 27 +- src/pages/cohorts.tsx | 363 +++++++++++++++++--- src/pages/login.tsx | 12 +- src/pages/tenant.tsx | 97 +++++- src/pages/tenantAdminSchema.json | 4 +- src/pages/userSchema.json | 4 - src/services/CohortService/cohortService.ts | 2 +- src/services/CreateUserService.ts | 2 +- src/services/Interceptor.ts | 4 +- src/services/UserList.ts | 21 +- src/utils/Helper.ts | 3 +- src/utils/Interfaces.ts | 4 +- src/utils/app.constant.ts | 3 + 24 files changed, 869 insertions(+), 313 deletions(-) create mode 100644 src/pages/cohortAdminSchema.json diff --git a/public/locales/en/common.json b/public/locales/en/common.json index e1d6ef64..64f5c9d5 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -14,6 +14,8 @@ "DUPLICATED_USER": "Duplicate User", "NO_USER_FOUND": "User not found", "NO_CENTER_FOUND": "Centers not found", + "NO_TENANT_FOUND": "Tenant Not Found", + "NO_COHORT_FOUND": "Cohort Not Found", "ALL": "All", "NO_GO_BACK": "No, go back", "YES": "Yes", @@ -208,13 +210,16 @@ "SEARCHBAR_PLACEHOLDER": "Search Center..", "COHORTS": "Cohorts", "CREATE_SUCCESSFULLY": "Cohort Created Successfully", + "COHORT_ADMIN_CREATE_SUCCESSFULLY": "Cohort Admin Created Successfully", + "COHORT_ADMIN_CREATE_FAILED": "Cohort Admin Created Failed", "CREATE_FAILED": "Cohort Creation Failed", "ADD_NEW_COHORT": "Add New Cohort", "UPDATE_COHORT": "Update Cohort", "SEARCH_COHORT": "Search Cohort", "DELETE_SUCCESSFULLY": "Cohort Deleted Successfully", "ALL_STATES": "All States", - "COHORT_MEMBER": "Cohort Member" + "COHORT_MEMBER": "Cohort Member", + "ADD_NEW_COHORT_ADMIN": "Add New Cohort Admin" }, "COURSE_PLANNER": { "GRADE": "Grade", @@ -425,7 +430,8 @@ "EMAIL": "Email", "USERNAME": "Username", "DOMAIN": "Domain", - "CREATE_TENANT_ADMIN": "Create Tenant Admin" + "CREATE_TENANT_ADMIN": "Create Tenant Admin", + "CREATE_COHORT_ADMIN": "Create Cohort Admin" }, "WORKSPACE": { "EDITOR_WORKSPACE": "Editor Workspace", @@ -444,16 +450,20 @@ "ROLE_ASSIGNED_fAILED": "Role Assigned Failed", "TENANT_MEMBER": "Tenant Member", "ALL_TENANTS": "All Tenants", - "ADD_NEW_TENANT_ADMIN": "Add New Tenant Admin" + "ADD_NEW_TENANT_ADMIN": "Add New Tenant Admin", + "TENANT_ADMIN_CREATE": "Tenant Admin Created Successfully", + "TENANT_ADMIN_FAILED_TO_CREATE": "Failed to Create Tenant Admin" }, "USER": { "ADD_NEW_USER": "Add New User", "UPDATE_USER": "Update User", "CREATE_SUCCESSFULLY": "User Created Successfully", + "FAILED_TO_CREATE": "User Creation Failed ", "DELETE_SUCCESSFULLY": "User Deleted Successfully", "UPDATE_SUCCESSFULLY": "User Updated Successfully", "FAILED_TO_UPDATE": "Failed to Update User", "FAILED_TO_DELETE:": "Failed to Delete User", + "USER_ALREADY_EXIST": "User already exists", "SEARCH": "Search User", "NO_USER_FOUND": "Users not found", "USER_NOT_FOUND": "User not found", diff --git a/src/components/ActionIcon.tsx b/src/components/ActionIcon.tsx index 71745926..09315c0f 100644 --- a/src/components/ActionIcon.tsx +++ b/src/components/ActionIcon.tsx @@ -1,12 +1,12 @@ import React from "react"; import { useTranslation } from "next-i18next"; -import { Box, Typography, Tooltip,useTheme, Button } from "@mui/material"; +import { Box, Typography, Tooltip, useTheme, Button } from "@mui/material"; import EditIcon from "@mui/icons-material/Edit"; import DeleteIcon from "@mui/icons-material/Delete"; -import deleteIcon from '../../public/images/deleteIcon.svg'; -import editIcon from '../../public/images/editIcon.svg'; -import cohortIcon from '../../public/images/apartment.svg'; -import addIcon from '../../public/images/addIcon.svg'; +import deleteIcon from "../../public/images/deleteIcon.svg"; +import editIcon from "../../public/images/editIcon.svg"; +import cohortIcon from "../../public/images/apartment.svg"; +import addIcon from "../../public/images/addIcon.svg"; import Image from "next/image"; @@ -18,9 +18,10 @@ interface ActionCellProps { rowData: any; disable: boolean; addAction?: boolean; - userAction?:boolean - roleButton?:boolean - onAdd:(rowData:any)=>void; + userAction?: boolean; + roleButton?: boolean; + allowEditIcon?: boolean; + onAdd: (rowData: any) => void; } const ActionIcon: React.FC = ({ @@ -29,14 +30,15 @@ const ActionIcon: React.FC = ({ onDelete, onAdd, reassignCohort, - roleButton=false, - addAction=false, - userAction=false, + roleButton = false, + addAction = false, + userAction = false, disable = false, - reassignType + allowEditIcon = false, + reassignType, }) => { const { t } = useTranslation(); - const theme = useTheme(); + const theme = useTheme(); return ( = ({ pointerEvents: disable ? "none" : "auto", }} > - {roleButton && ( - - - -)} + {roleButton && ( + + + + )} - {addAction&& - { - onAdd(rowData); - }} - sx={{ - display: "flex", - flexDirection: "column", - alignItems: "center", - cursor: "pointer", - color: disable ? theme?.palette?.secondary.contrastText : "", - backgroundColor:"#EAF2FF", - p:"10px" - }} - > - -{/* + {addAction && ( + + { + onAdd(rowData); + }} + sx={{ + display: "flex", + flexDirection: "column", + alignItems: "center", + cursor: "pointer", + color: disable ? theme?.palette?.secondary.contrastText : "", + backgroundColor: "#EAF2FF", + p: "10px", + }} + > + + {/* {t("COMMON.DELETE")} */} - - } - - { - onEdit(rowData); - }} - sx={{ - display: "flex", - flexDirection: "column", - alignItems: "center", - cursor: "pointer", - color: disable ? theme?.palette?.secondary.contrastText : "", - backgroundColor:"#E3EAF0", - p:"10px" - - - }} - > - - {/* + + + )} + {!allowEditIcon && ( + <> + + { + onEdit(rowData); + }} + sx={{ + display: "flex", + flexDirection: "column", + alignItems: "center", + cursor: "pointer", + color: disable ? theme?.palette?.secondary.contrastText : "", + backgroundColor: "#E3EAF0", + p: "10px", + }} + > + + {/* {t("COMMON.EDIT")} */} - - - - { - onDelete(rowData); - }} - sx={{ - display: "flex", - flexDirection: "column", - alignItems: "center", - cursor: "pointer", - color: disable ? theme?.palette?.secondary.contrastText : "", - backgroundColor:"#EAF2FF", - p:"10px" - }} - > - -{/* + + + + + { + onDelete(rowData); + }} + sx={{ + display: "flex", + flexDirection: "column", + alignItems: "center", + cursor: "pointer", + color: disable ? theme?.palette?.secondary.contrastText : "", + backgroundColor: "#EAF2FF", + p: "10px", + }} + > + + {/* {t("COMMON.DELETE")} */} - - + + + + )} - { userAction && ( - { - if(reassignCohort) - reassignCohort(rowData); - }} - sx={{ - display: "flex", - flexDirection: "column", - alignItems: "center", - cursor: "pointer", - color: disable ? theme?.palette?.secondary.contrastText : "", - backgroundColor:"#E5E5E5", - p:"10px" - }} - > - -{/* + {userAction && ( + + { + if (reassignCohort) reassignCohort(rowData); + }} + sx={{ + display: "flex", + flexDirection: "column", + alignItems: "center", + cursor: "pointer", + color: disable ? theme?.palette?.secondary.contrastText : "", + backgroundColor: "#E5E5E5", + p: "10px", + }} + > + + {/* {t("COMMON.DELETE")} */} - - )} - + + + )} ); }; diff --git a/src/components/AddNewTenant.tsx b/src/components/AddNewTenant.tsx index ae6ebf2e..170edf9a 100644 --- a/src/components/AddNewTenant.tsx +++ b/src/components/AddNewTenant.tsx @@ -39,18 +39,16 @@ interface AddLearnerModalProps { } const uiSchema = { - district: { - "ui:widget": "", - "ui:options": {}, - }, - status: { - "ui:widget": "CustomRadioWidget", - "ui:options": {}, + name: { + "ui:widget": "text", + "ui:placeholder": "Enter your full name", + // "ui:help": "Only letters and spaces are allowed.", }, - block: { - "ui:widget": "", - "ui:options": {}, + domain: { + "ui:widget": "text", + "ui:placeholder": "Enter the domain name for your tenant", + "ui:help": "This will be the unique identifier for your tenant.", }, }; @@ -62,7 +60,6 @@ const AddNewCenters: React.FC = ({ userId, }) => { const [schema] = useState(Tenatschema); - const [showForm, setShowForm] = useState(false); const { t } = useTranslation(); const [updateBtnDisabled, setUpdateBtnDisabled] = React.useState(true); @@ -88,14 +85,17 @@ const AddNewCenters: React.FC = ({ showToastMessage("Form data is required", "error"); return; } + console.log({ formData }); const cohortDetails: CohortDetails = { name: formData?.name, - domain: formData?.domain, + domain: formData?.domain ? formData?.domain : " ", // status: formData?.status, }; const cohortData = await tenantCreate(cohortDetails); + console.log({ cohortData }); + if (cohortData?.responseCode === 200 || cohortData?.responseCode === 201) { showToastMessage(t("TENANT.CREATE_SUCCESSFULLY"), "success"); onClose(); @@ -131,7 +131,7 @@ const AddNewCenters: React.FC = ({ customFields={customFields} onChange={handleChange} onError={handleError} - id="new-center-form" + // id="new-center-form" > = ({ lg={inModal ? 12 : isCenterSelection ? 6 : 6} > - tenant.label?.toLowerCase().charAt(0).toUpperCase() + - tenant.label?.toLowerCase().slice(1) - )} - codes={tenants?.map((tenant: any) => tenant.value)} + names={ + Array.isArray(tenants) + ? tenants.map( + (tenant: any) => + tenant.label?.toLowerCase().charAt(0).toUpperCase() + + tenant.label?.toLowerCase().slice(1) + ) + : [] + } + codes={ + Array.isArray(tenants) + ? tenants.map((tenant: Tenant) => tenant.value) + : [] + } tagName={t("TENANT.TENANT_MEMBER")} selectedCategories={selectedTenant} onCategoryChange={handleTenantChange} @@ -200,16 +208,25 @@ const AreaSelection: React.FC = ({ lg={inModal ? 12 : isCenterSelection ? 6 : 6} > cohort.label)} - codes={cohorts?.map((cohort) => cohort.value)} + names={ + Array.isArray(cohorts) + ? cohorts.map((cohort: any) => cohort.label) + : [] + } + codes={ + Array.isArray(cohorts) + ? cohorts.map((cohort: any) => cohort.cohortId) + : [] + } tagName={t("COHORTS.COHORT_MEMBER")} selectedCategories={selectedCohort} onCategoryChange={handleCohortChange} - disabled={ - cohorts?.length <= 0 || - (selectedTenant?.length === 0 && - tenantDefaultValue === t("COHORTS.ALL_STATES")) - } + // disabled={ + // !Array.isArray(cohorts) || + // cohorts.length <= 0 || + // (selectedTenant?.length === 0 && + // tenantDefaultValue === t("COHORTS.ALL_STATES")) + // } overall={!inModal} defaultValue={cohortDefaultValue} /> diff --git a/src/components/FormControl.tsx b/src/components/FormControl.tsx index d6aa0c4a..80ec929e 100644 --- a/src/components/FormControl.tsx +++ b/src/components/FormControl.tsx @@ -59,7 +59,7 @@ const MultipleSelectCheckmarks: React.FC = ({ selectedNames = defaultValue ? [defaultValue] : []; } - const selectedCodes = selectedNames?.map( + const selectedCodes = selectedNames.map( (name) => codes[names.indexOf(name)] ); @@ -74,19 +74,36 @@ const MultipleSelectCheckmarks: React.FC = ({ labelId="multiple-checkbox-label" id="multiple-checkbox" value={ - selectedCategories?.length === 0 || selectedCategories[0] === "" - ? defaultValue - ? [defaultValue] - : "" - : selectedCategories + // If no categories are selected (empty or invalid selection), show default value or the first name from 'names' + selectedCategories?.length <= 0 || selectedCategories[0] === "" + ? names?.length > 1 + ? [names[0]] // If there are multiple tenants, default to the first one + : defaultValue + ? [defaultValue] + : [] // Else, use defaultValue or empty array + : selectedCategories // If categories are selected, use selectedCategories } - onChange={handleChange} + onChange={handleChange} // Handle the change event for the selection input={} renderValue={(selected) => { + // Ensure selected is always an array, even if one item is selected const selectedArray = Array.isArray(selected) ? selected : [selected]; - return selectedArray.join(", "); + + // Get the corresponding names for the selected tenant IDs + const selectedNames = selectedArray + .map((tenantId) => { + const index = codes.indexOf(tenantId); // Find the corresponding name using `codes` + return index >= 0 ? names[index] : tenantId; // Map tenantId to name or return tenantId if not found + }) + .filter((name) => name !== ""); // Filter out empty values + + // Return single or multiple selected names + if (selectedNames.length === 1) { + return selectedNames[0]; // Return single selected name + } + return selectedNames.join(", "); // Join multiple selected names with commas }} MenuProps={MenuProps} > @@ -96,6 +113,7 @@ const MultipleSelectCheckmarks: React.FC = ({ )} + {/* Render menu items for available names */} {names?.map((name) => ( diff --git a/src/components/HeaderComponent.tsx b/src/components/HeaderComponent.tsx index 8f55744c..3aad3cf8 100644 --- a/src/components/HeaderComponent.tsx +++ b/src/components/HeaderComponent.tsx @@ -31,6 +31,7 @@ import { userAgent } from "next/server"; interface State { value: string; label: string; + tenantId?: string; } interface District { @@ -278,7 +279,7 @@ const HeaderComponent = ({ const stateField = JSON.parse(admin).customFields.find( (field: any) => field.label === "STATES" ); - if (stateField.value.includes(",")) { + if (stateField?.value?.includes(",")) { setStateDefaultValue(t("COMMON.ALL_STATES")); } else { setStateDefaultValue(stateField.value); diff --git a/src/components/KaTableComponent.tsx b/src/components/KaTableComponent.tsx index 33390dc4..5ff46f1e 100644 --- a/src/components/KaTableComponent.tsx +++ b/src/components/KaTableComponent.tsx @@ -33,6 +33,7 @@ interface KaTableComponentProps { onEdit?: any; onAdd?: any; addBtnFunc?: any; + addCohortBtnFunc?: any; addAction?: boolean; roleButton?: boolean; reassignCohort?: any; @@ -48,6 +49,7 @@ interface KaTableComponentProps { pagination?: boolean; reassignType?: string; handleMemberClick?: any; + allowEditIcon?: boolean; } const KaTableComponent: React.FC = ({ @@ -61,6 +63,7 @@ const KaTableComponent: React.FC = ({ onDelete, onAdd, addBtnFunc, + addCohortBtnFunc, addAction, roleButton, reassignCohort, @@ -69,6 +72,7 @@ const KaTableComponent: React.FC = ({ pagination = true, reassignType, handleMemberClick, + allowEditIcon, }) => { const [selectedRowIds, setSelectedRowIds] = useState([]); const { t } = useTranslation(); @@ -142,6 +146,7 @@ const KaTableComponent: React.FC = ({ addAction={addAction} reassignCohort={reassignCohort} onDelete={onDelete} + allowEditIcon={allowEditIcon} // userAction={props.rowData?.userId} disable={props.rowData?.status === Status.ARCHIVED} reassignType={reassignType} @@ -186,6 +191,40 @@ const KaTableComponent: React.FC = ({ ); } + if ( + props.column.key === DataKey.CREATE_COHORT_ADMIN && + roleButton + ) { + return ( + + + + ); + } if ( props.column.key === DataKey?.UPDATED_AT && props.rowData?.updatedAt diff --git a/src/components/TenantSchema.json b/src/components/TenantSchema.json index 11cb4a23..9a70ee14 100644 --- a/src/components/TenantSchema.json +++ b/src/components/TenantSchema.json @@ -2,7 +2,7 @@ "title": "", "description": "", "type": "object", - "required": ["name", "status"], + "required": ["name"], "properties": { "name": { "title": "Tenant Name", @@ -11,18 +11,10 @@ "validation": [], "description": "Enter tenant name." }, - "status": { - "title": "Status", + "domain": { + "title": "Domain", "type": "string", - "oneOf": [ - { "const": "active", "title": "Active" }, - { "const": "inactive", "title": "Inactive" }, - { "const": "archived", "title": "Archived" } - ], - "default": "active", - "readonly": true, - "validation": [], - "description": "The current status of the tenant, this field is read-only." + "fieldId": null } }, "dependencies": {} diff --git a/src/components/UserTable.tsx b/src/components/UserTable.tsx index 0a89c33d..02af66f6 100644 --- a/src/components/UserTable.tsx +++ b/src/components/UserTable.tsx @@ -11,9 +11,8 @@ import Typography from "@mui/material/Typography"; import { DataType, SortDirection } from "ka-table/enums"; import { useTranslation } from "next-i18next"; import React, { useEffect, useState } from "react"; -import KaTableComponent from "../components/KaTableComponent"; -import Loader from "../components/Loader"; -import { getCohortList } from "../services/GetCohortList"; +import KaTableComponent from "./KaTableComponent"; +import Loader from "./Loader"; import { userList } from "../services/UserList"; import PersonSearchIcon from "@mui/icons-material/PersonSearch"; import { Role, apiCatchingDuration } from "@/utils/app.constant"; @@ -31,6 +30,8 @@ import { useQuery } from "@tanstack/react-query"; import ReassignCenterModal from "./ReassignCenterModal"; import { deleteUser, + getCohortList, + getTenantLists, rolesList, updateCohortMemberStatus, updateCohortUpdate, @@ -77,13 +78,14 @@ type UserDetailParam = { }; type FilterDetails = { - // role: any; + role: any; status?: any; districts?: any; states?: any; blocks?: any; name?: any; cohortId?: any; + tenantId?: any; }; interface CenterProp { cohortId: string; @@ -172,7 +174,6 @@ const UserTable: React.FC = ({ const [isReassignCohortModalOpen, setIsReassignCohortModalOpen] = useState(false); const [centers, setCenters] = useState([]); - const [userName, setUserName] = useState(""); const [blocks, setBlocks] = useState([]); const [userCohort, setUserCohorts] = useState(""); const [assignedCenters, setAssignedCenters] = useState(); @@ -182,16 +183,16 @@ const UserTable: React.FC = ({ const [district, setDistrict] = useState(""); const [blockCode, setBlockCode] = useState(""); const [districtCode, setDistrictCode] = useState(""); - const [selectedReason, setSelectedReason] = useState(""); - const [otherReason, setOtherReason] = useState(""); const [deleteUserState, setDeleteUserState] = useState(false); const [editUserState, setEditUserState] = useState(false); const [selectedCenter, setSelectedCenter] = useState([]); const [selectedCenterCode, setSelectedCenterCode] = useState([]); const [schema, setSchema] = React.useState(userJsonSchema); - const [enableCenterFilter, setEnableCenterFilter] = useState(false); + const [listOfCohorts, setListOfCohorts] = useState([]); const [updateBtnDisabled, setUpdateBtnDisabled] = React.useState(true); - + const [listOfTenants, setListOfTenants] = useState([]); + const [selectedTenant, setSelectedTenant] = React.useState([]); + const [selectedCohort, setSelectedCohort] = React.useState([]); const isMobile: boolean = useMediaQuery((theme: Theme) => theme.breakpoints.down("sm") ); @@ -289,10 +290,37 @@ const UserTable: React.FC = ({ setUpdateBtnDisabled(true); }; const [filters, setFilters] = useState({ - // role: role, + role: "learner", status: [statusValue], }); + useEffect(() => { + const fetchData = async () => { + try { + const result = await getTenantLists(filters); + setListOfTenants(result); + let data = { + limit: 0, + offset: 0, + filters: { + tenantId: result?.[0]?.tenantId, + // cohortId: cohortId, + }, + }; + if (filters?.tenantId) { + const cohortList = await getCohortList(data); + setListOfCohorts(cohortList?.results); + } + } catch (error) { + console.error("Error fetching data:", error); + } + }; + + if (filters) { + fetchData(); + } + }, [filters]); + const handleChange = (event: SelectChangeEvent) => { setPageSize(event.target.value); setPageLimit(Number(event.target.value)); @@ -350,7 +378,7 @@ const UserTable: React.FC = ({ }); setSelectedCenterCode([]); - setEnableCenterFilter(false); + // setEnableCenterFilter(false); setSelectedDistrict([]); setSelectedCenter([]); @@ -363,7 +391,7 @@ const UserTable: React.FC = ({ if (filters.status) setFilters({ status: [filters.status], - // role: role + role: "learner", }); // else setFilters({ role: role }); else setFilters({}); @@ -373,13 +401,13 @@ const UserTable: React.FC = ({ if (filters.status) setFilters({ states: stateCodes, - // role: role, + role: "learner", status: filters.status, }); else setFilters({ states: stateCodes, - // role: role + role: "learner", }); } }; @@ -420,7 +448,7 @@ const UserTable: React.FC = ({ setSelectedCenterCode([]); - setEnableCenterFilter(false); + // setEnableCenterFilter(false); setSelectedCenter([]); setSelectedBlock([]); @@ -433,13 +461,13 @@ const UserTable: React.FC = ({ if (filters.status) { setFilters({ // states: selectedStateCode, - // role: role, + role: "learner", status: filters.status, }); } else { setFilters({ // states: selectedStateCode, - // role: role, + role: "learner", }); } if (newQuery.district) { @@ -467,14 +495,14 @@ const UserTable: React.FC = ({ setFilters({ // states: selectedStateCode, // districts: districts, - // role: role, + role: "learner", status: filters.status, }); } else { setFilters({ // states: selectedStateCode, // districts: districts, - // role: role, + role: "learner", }); } } @@ -482,7 +510,7 @@ const UserTable: React.FC = ({ const handleBlockChange = (selected: string[], code: string[]) => { setSelectedCenterCode([]); - setEnableCenterFilter(false); + // setEnableCenterFilter(false); setSelectedCenter([]); const newQuery = { ...router.query }; if (newQuery.center) { @@ -511,14 +539,14 @@ const UserTable: React.FC = ({ setFilters({ // states: selectedStateCode, // districts: selectedDistrictCode, - // role: role, + role: "learner", status: filters.status, }); } else { setFilters({ // states: selectedStateCode, // districts: selectedDistrictCode, - // role: role, + role: "learner", }); } } else { @@ -538,7 +566,7 @@ const UserTable: React.FC = ({ // states: selectedStateCode, // districts: selectedDistrictCode, // blocks: blocks, - // role: role, + role: "learner", status: filters.status, }); } else { @@ -546,7 +574,7 @@ const UserTable: React.FC = ({ // states: selectedStateCode, // districts: selectedDistrictCode, // blocks: blocks, - // role: role, + role: "learner", }); } } @@ -579,14 +607,14 @@ const UserTable: React.FC = ({ localStorage.setItem("selectedCenter", selected[0]); setSelectedCenterStore(selected[0]); if (selected[0] === "" || selected[0] === t("COMMON.ALL_CENTERS")) { - setEnableCenterFilter(false); + // setEnableCenterFilter(false); setSelectedCenterCode([]); if (filters.status) { setFilters({ // states: selectedStateCode, // districts: selectedDistrictCode, // blocks: selectedBlockCode, - // role: role, + role: "learner", status: filters.status, }); } else { @@ -594,18 +622,18 @@ const UserTable: React.FC = ({ // states: selectedStateCode, // districts: selectedDistrictCode, // blocks: selectedBlockCode, - // role: role, + role: "learner", }); } } else { - setEnableCenterFilter(true); + // setEnableCenterFilter(true); setFilters({ // states: selectedStateCode, // districts: selectedDistrictCode, // blocks: blocks, cohortId: code[0], - // role: role, + role: "learner", status: [statusValue], }); } @@ -760,22 +788,29 @@ const UserTable: React.FC = ({ const fetchUserList = async () => { setLoading(true); try { - const fields = ["age", "districts", "states", "blocks", "gender"]; + // const fields = ["age", "districts", "states", "blocks", "gender"]; let limit = pageLimit; let offset = pageOffset * limit; if (filters.name) { offset = 0; } - const objData = { + + const payload = { limit, - filters, + filters: { + role: filters.role, + status: filters.status, + }, + tenantCohortRoleMapping: { + tenantId: filters?.tenantId, + cohortId: filters?.cohortId ? [filters?.cohortId] : [], + }, sort: sortBy, offset, - fields, }; - const resp = await userList(objData); + const resp = await userList({ payload }); if (resp?.totalCount >= 15) { setPagination(true); @@ -1292,7 +1327,45 @@ const UserTable: React.FC = ({ }; const handleError = (error: any) => {}; + const handleTenantChange = ( + selectedNames: string[], // An array of selected tenant names + selectedCodes: string[] // An array of selected tenant IDs + ) => { + if (selectedNames && selectedCodes) { + // Join the tenant IDs into a comma-separated string + const tenantId = selectedCodes.join(","); + + setSelectedTenant(selectedNames); + setFilters((prevFilter) => ({ + ...prevFilter, + tenantId: tenantId, + })); + } else { + console.log("No valid tenants selected"); + } + }; + + const handleCohortChange = ( + selectedNames: string[], + selectedCodes: string[] + ) => { + console.log({ selectedCodes }); + + if (selectedNames && selectedCodes) { + const cohortId = selectedCodes.join(","); + setSelectedCohort(selectedNames); + setFilters((prevFilter) => ({ + ...prevFilter, + cohortId: cohortId, + })); + } else { + console.log("No valid cohort selected"); + } + }; + const userProps = { + tenants: listOfTenants, + cohorts: listOfCohorts, showAddNew: false, showSort: true, userType: userType, @@ -1304,8 +1377,11 @@ const UserTable: React.FC = ({ setSelectedBlock: setSelectedBlock, selectedSort: selectedSort, statusValue: statusValue, + selectedTenant: selectedTenant, + selectedCohort: selectedCohort, + handleTenantChange: handleTenantChange, // setStatusValue: setStatusValue, - handleStateChange: handleStateChange, + handleCohortChange: handleCohortChange, handleDistrictChange: handleDistrictChange, handleBlockChange: handleBlockChange, handleSortChange: handleSortChange, diff --git a/src/data/tableColumns.ts b/src/data/tableColumns.ts index 0f79978b..b32a0839 100644 --- a/src/data/tableColumns.ts +++ b/src/data/tableColumns.ts @@ -70,8 +70,11 @@ export const getTLTableColumns = (t: any, isMobile: boolean) => { return generateColumns(t, configs, isMobile); }; -export const getTenantTableData = (t: any, isMobile: boolean,role:string) => { +export const getTenantTableData = (t: any, isMobile: boolean,role:any) => { console.log({role}); + const adminInfo = JSON.parse(localStorage.getItem("adminInfo") ?? '{}'); + // const localRoleCheck = adminInfo?.tenantData?.[0]?.roleName === "cohort admin"; + const isCohortAdmin = adminInfo?.tenantData?.[0]?.roleName === "cohort admin" ? true : false; const configs: ColumnConfig[] = [ { key: "name", titleKey: "TABLE_TITLE.NAME", width: 130 }, @@ -92,15 +95,15 @@ export const getTenantTableData = (t: any, isMobile: boolean,role:string) => { // }, { key: "status", titleKey: "TABLE_TITLE.STATUS", width: 90 }, - ...(role === "super_admin" ? [{ key: "roleDefine", titleKey: "TABLE_TITLE.CREATE_TENANT_ADMIN", width: 130 }] : []), - - ...(role==="super_admin" ?[{ key: "actions", titleKey: "TABLE_TITLE.ACTIONS", width: 125 }]:[]), + ...(role == true ? [{ key: "roleDefine", titleKey: "TABLE_TITLE.CREATE_TENANT_ADMIN", width: 130 }] : []), + ...(isCohortAdmin ==false?[{ key: "actions", titleKey: "TABLE_TITLE.ACTIONS", width: 125 }]:[]), + // ...(role== true ?[{ key: "actions", titleKey: "TABLE_TITLE.ACTIONS", width: 125 }]:[]), ]; return generateColumns(t, configs, isMobile); }; -export const getCohortTableData = (t: any, isMobile: boolean) => { +export const getCohortTableData = (t: any, isMobile: boolean,role:any) => { const configs: ColumnConfig[] = [ { key: "name", titleKey: "TABLE_TITLE.NAME", width: 130 }, { key: "type", titleKey: "TABLE_TITLE.TYPE", width: 90 }, @@ -120,6 +123,7 @@ export const getCohortTableData = (t: any, isMobile: boolean) => { // width: 130, // }, // { key: "roleDefine", titleKey: "TABLE_TITLE.ROLE", width: 130 }, + ...(role == true ? [{ key: "cohortAdmin", titleKey: "TABLE_TITLE.CREATE_COHORT_ADMIN", width: 130 }] : []), { key: "actions", titleKey: "TABLE_TITLE.ACTIONS", width: 125 }, ]; diff --git a/src/pages/cohortAdminSchema.json b/src/pages/cohortAdminSchema.json new file mode 100644 index 00000000..7bc668a9 --- /dev/null +++ b/src/pages/cohortAdminSchema.json @@ -0,0 +1,54 @@ +{ + "type": "object", + "required": ["name", "username", "password"], + "properties": { + "name": { + "title": "Cohort Name", + "type": "string", + "fieldId": null, + "validation": [], + "description": "Enter the cohort name." + }, + "mobileNo": { + "title": "Mobile Number", + "fieldId": null, + "type": "string", + "pattern": "^[6-9]\\d{9}$", + "validation": ["mobile"] + }, + "email": { + "title": "Email Address", + "type": "string", + "format": "email", + "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$", + "validation": ["required"] + }, + "username": { + "title": "Username", + "fieldId": null, + "type": "string", + "validation": [] + }, + "password": { + "title": "Password", + "fieldId": null, + "type": "string", + "validation": [] + }, + + "role": { + "title": "Role", + "type": "string", + "readOnly": true, + "oneOf": [ + { + "title": "Cohort Admin", + "const": "cohort_admin" + } + ], + "default": "cohort_admin", + "validation": [] + } + }, + "dependencies": {} +} diff --git a/src/pages/cohortSchema.json b/src/pages/cohortSchema.json index 1f4c3537..9bd6a469 100644 --- a/src/pages/cohortSchema.json +++ b/src/pages/cohortSchema.json @@ -9,6 +9,18 @@ "validation": [], "description": "Enter the cohort name." }, + "username": { + "title": "Username", + "fieldId": null, + "type": "string", + "validation": [] + }, + "password": { + "title": "Password", + "fieldId": null, + "type": "string", + "validation": [] + }, "type": { "title": "Type", "type": "string", @@ -17,6 +29,7 @@ "validation": [], "description": "Indicates the type of user or entity." }, + "status": { "title": "Status", "type": "string", @@ -26,9 +39,21 @@ { "const": "archived", "title": "Archived" } ], "default": "active", - "readonly": true, + "readOnly": true, "validation": [], "description": "The current status of the cohort, this field is read-only." + }, + + "role": { + "title": "Role", + "type": "string", + "oneOf": [ + { + "title": "Learner", + "const": "learner" + } + ], + "validation": [] } }, "dependencies": {} diff --git a/src/pages/cohorts.tsx b/src/pages/cohorts.tsx index e76e9261..03141d9b 100644 --- a/src/pages/cohorts.tsx +++ b/src/pages/cohorts.tsx @@ -11,6 +11,7 @@ import { deleteCohort, fetchCohortMemberList, getCohortList, + getTenantLists, rolesList, updateCohortUpdate, userCreate, @@ -55,6 +56,7 @@ import { useRouter } from "next/router"; import cohortSchema from "./cohortSchema.json"; import AddIcon from "@mui/icons-material/Add"; import userJsonSchema from "./userSchema.json"; +import cohortASchema from "./cohortAdminSchema.json"; type cohortFilterDetails = { type?: string; @@ -91,7 +93,8 @@ const Center: React.FC = () => { ); // handle states - const [selectedState, setSelectedState] = React.useState([]); + const [cohortAdminSchema] = useState(cohortASchema); + const [selectedTenant, setSelectedTenant] = React.useState([]); const [selectedDistrict, setSelectedDistrict] = React.useState([]); const [selectedBlock, setSelectedBlock] = React.useState([]); const [selectedSort, setSelectedSort] = useState("Sort"); @@ -107,12 +110,12 @@ const Center: React.FC = () => { const [inputName, setInputName] = React.useState(""); const [loading, setLoading] = useState(undefined); const [userId, setUserId] = useState(""); + const [adminRole, setAdminRole] = useState(); const [schema] = React.useState(cohortSchema); const [userSchema, setUserSchema] = React.useState(userJsonSchema); const [openAddNewCohort, setOpenAddNewCohort] = React.useState(false); const [statusValue, setStatusValue] = useState(Status.ACTIVE); - const [pageCount, setPageCount] = useState(Numbers.ONE); const [pageOffset, setPageOffset] = useState(Numbers.ZERO); const [pageLimit, setPageLimit] = useState(Numbers.TEN); @@ -125,12 +128,16 @@ const Center: React.FC = () => { const [formdata, setFormData] = useState(); const [totalCount, setTotalCound] = useState(0); const [editFormData, setEditFormData] = useState([]); + const [listOfTenants, setListOfTenants] = useState([]); const [isEditForm, setIsEditForm] = useState(false); const [selectedRowData, setSelectedRowData] = useState(""); const [Addmodalopen, setAddmodalopen] = React.useState(false); const [updateBtnDisabled, setUpdateBtnDisabled] = React.useState(true); const [addFormData, setAddFormData] = useState({}); const [addBtnDisabled, setAddBtnDisabled] = useState(true); + const [previousTenantId, setPreviousTenantId] = useState(null); + const [isCreateCohortAdminModalOpen, setIsCreateCohortAdminModalOpen] = + useState(false); const selectedBlockStore = useSubmittedButtonStore( (state: any) => state.selectedBlockStore @@ -236,9 +243,48 @@ const Center: React.FC = () => { // // "ui:help": "Date of birth in YYYY-MM-DD format.", // }, }; + const cohortAdminUiSchema = { + name: { + "ui:widget": "text", + "ui:placeholder": "Enter your full name", + "ui:help": "Full name, numbers, letters and spaces are allowed.", + }, + username: { + "ui:widget": "text", + "ui:placeholder": "Enter your username", + "ui:help": "Username must be at least 3 characters long.", + }, + password: { + "ui:widget": "password", + "ui:placeholder": "Enter a secure password", + "ui:help": + "Password must be at least 8 characters long, with at least one letter and one number.", + }, + role: { + "ui:widget": "select", + "ui:placeholder": "Select a role", + // "ui:help": "Select a role.", + }, + mobileNo: { + "ui:widget": "text", + "ui:placeholder": "Mobile number", + "ui:help": "Enter a valid 10-digit mobile number.", + }, + email: { + // "ui:widget": "text", + "ui:placeholder": "Enter your email address", + "ui:help": "Enter a valid email address.", + // "ui:options": {}, + }, + // dob: { + // "ui:widget": "date", + // "ui:placeholder": "Select your date of birth", + // // "ui:help": "Date of birth in YYYY-MM-DD format.", + // }, + }; const [filters, setFilters] = useState({ - type: "cohort", + type: CohortTypes.COHORT, // states: "", status: [statusValue], // districts: "", @@ -278,9 +324,14 @@ const Center: React.FC = () => { }; useEffect(() => { - if (typeof window !== "undefined" && window.localStorage) { - const userId = localStorage.getItem(Storage.USER_ID) || ""; - setUserId(userId); + if (typeof window !== "undefined") { + const getRole = JSON.parse(localStorage.getItem("adminInfo") || "{}"); + setAdminRole( + getRole?.tenantData?.[0]?.roleName == "tenant admin" + ? true + : false || getRole?.isSuperAdmin + ); + setUserId(getRole?.userId); } // getAddCenterFormData(); @@ -288,38 +339,38 @@ const Center: React.FC = () => { getAdminInformation(); }, []); - useEffect(() => { - const fetchRoles = async () => { - const obj = { - limit: "10", - page: 1, - filters: { - tenantId: selectedRowData?.tenantId, - }, - }; - const response = await rolesList(obj); - - if (response?.result) { - const rolesOptions = response.result.map((role: any) => ({ - const: role.roleId, - title: role.title, - })); - - setUserSchema((prevSchema) => ({ - ...prevSchema, - properties: { - ...prevSchema.properties, - role: { - ...prevSchema.properties.role, - oneOf: rolesOptions, - }, - }, - })); - } - }; + // useEffect(() => { + // const fetchRoles = async () => { + // const obj = { + // limit: "10", + // page: 1, + // filters: { + // tenantId: selectedRowData?.tenantId, + // }, + // }; + // const response = await rolesList(obj); + + // if (response?.result) { + // const rolesOptions = response.result.map((role: any) => ({ + // const: role.roleId, + // title: role.title, + // })); + + // setUserSchema((prevSchema) => ({ + // ...prevSchema, + // properties: { + // ...prevSchema.properties, + // role: { + // ...prevSchema.properties.role, + // oneOf: rolesOptions, + // }, + // }, + // })); + // } + // }; - fetchRoles(); - }, [Addmodalopen]); + // fetchRoles(); + // }, [Addmodalopen]); const fetchUserList = async () => { setLoading(true); @@ -341,7 +392,7 @@ const Center: React.FC = () => { const resp = await getCohortList(data); if (resp) { - const result = resp?.results?.cohortDetails; + const result = resp?.results; // Map response data to required format const resultData = result?.map((item: any) => ({ @@ -444,14 +495,18 @@ const Center: React.FC = () => { }; useEffect(() => { - if ( - selectedBlockCode !== "" || - (selectedDistrictCode !== "" && selectedBlockCode === "") - ) { - fetchUserList(); - } - fetchUserList(); - // getFormData(); + const fetchData = async () => { + if ( + selectedBlockCode !== "" || + (selectedDistrictCode !== "" && selectedBlockCode === "") + ) { + await fetchUserList(); + } + await fetchUserList(); + // getFormData(); + }; + + fetchData(); }, [ pageOffset, pageLimit, @@ -680,11 +735,27 @@ const Center: React.FC = () => { } }; - const handleTenantChange = (selected: any) => { - console.log({ selected }); + const handleTenantChange = ( + selectedNames: string[], + selectedCodes: string[] + ) => { + if (selectedNames && selectedCodes) { + const tenantId = selectedCodes.join(","); + + setSelectedTenant(selectedNames); + + setFilters((prevFilter) => ({ + ...prevFilter, + tenantId: tenantId, + })); + } else { + console.log("No valid tenants selected"); + } }; + const handleCloseModal = () => { setConfirmationModalOpen(false); + setIsCreateCohortAdminModalOpen(false); setUpdateBtnDisabled(true); }; @@ -804,6 +875,7 @@ const Center: React.FC = () => { offset: 0, }; const resp = await getCohortList(data); + setFormData(rowData); setEditFormData(mapFields(schema, rowData)); setLoading(false); setIsEditForm(true); @@ -829,11 +901,21 @@ const Center: React.FC = () => { }; const handleAdd = (rowData: any) => { + console.log("fnction called"); + + // const tenantId = rowData?.tenantId; + // const currentTenantId = localStorage.getItem("tenantId"); + // setPreviousTenantId(currentTenantId); + // if (tenantId) { + // localStorage.setItem("tenantId", tenantId); + // console.log("Modal opened, Tenant ID set:", tenantId); + // } setSelectedRowData({ ...rowData }); setLoading(true); setAddmodalopen(true); setLoading(false); }; + console.log({ previousTenantId }); // add extra buttons const extraActions: any = [ @@ -1040,6 +1122,13 @@ const Center: React.FC = () => { } }; const handleAddmodal = () => { + // if (previousTenantId) { + // localStorage.setItem("tenantId", previousTenantId); + // console.log("Modal closed, Tenant ID restored:", previousTenantId); + // } else { + // localStorage.removeItem("tenantId"); + // console.log("Modal closed, Tenant ID removed"); + // } setAddmodalopen(false); setAddBtnDisabled(true); setAddFormData({}); @@ -1065,6 +1154,19 @@ const Center: React.FC = () => { }>; } + const roleObj = { + limit: "10", + page: 1, + filters: { + tenantId: selectedRowData?.tenantId, + }, + }; + const response = await rolesList(roleObj); + const matchedRole = response?.result?.find( + (role: any) => role.code === formData?.role + ); + const roleId = matchedRole ? matchedRole?.roleId : null; + let obj: UserCreateData = { name: formData?.name, mobile: formData?.mobileNo, @@ -1073,23 +1175,26 @@ const Center: React.FC = () => { password: formData?.password, tenantCohortRoleMapping: [ { - roleId: formData?.role, + roleId: roleId, tenantId: selectedRowData?.tenantId, cohortId: [selectedRowData?.cohortId], }, ], }; - const resp = await userCreate(obj as any); + const resp = await userCreate(obj as any, selectedRowData?.tenantId); + console.log({ selectedRowData, matchedRole, formData, response, resp }); if (resp?.responseCode === 200 || resp?.responseCode === 201) { - showToastMessage(t("COHORTS.CREATE_SUCCESSFULLY"), "success"); + showToastMessage(t("USER.CREATE_SUCCESSFULLY"), "success"); setLoading(false); + } else if (resp?.responseCode === 403) { + showToastMessage(t("USER.USER_ALREADY_EXIST"), "error"); } else { - showToastMessage(t("COHORTS.CREATE_FAILED"), "error"); + showToastMessage(t("USER.FAILED_TO_CREATE"), "error"); } } catch (error) { console.error("Error updating cohort:", error); - showToastMessage(t("COHORTS.CREATE_FAILED"), "error"); + showToastMessage(t("USER.FAILED_TO_CREATE"), "error"); } finally { setLoading(false); setConfirmButtonDisable(false); @@ -1098,14 +1203,91 @@ const Center: React.FC = () => { setIsEditForm(false); } }; + useEffect(() => { + const fetchData = async () => { + const result = await getTenantLists(filters); + setListOfTenants(result); + }; + + fetchData(); + }, [filters]); + + const handleCreateCohortAdmin = (rowData: any) => { + setSelectedRowData(rowData); + setIsCreateCohortAdminModalOpen(true); + }; + + const handleAddCohortAdminAction = async ( + data: IChangeEvent, + event: React.FormEvent + ) => { + setLoading(true); + const formData = data?.formData; + + try { + setLoading(true); + setConfirmButtonDisable(true); + + const roleObj = { + limit: "10", + page: 1, + filters: { + tenantId: selectedRowData?.tenantId, + }, + }; + const response = await rolesList(roleObj); + console.log({ selectedRowData, response, formData }); + + const cohortAdminRole = response?.result.find( + (item: any) => item.code === "cohort_admin" + ); + + let obj = { + name: formData?.name, + username: formData?.username, + password: formData?.password, + mobile: formData?.mobileNo ? formData?.mobileNo : "", + email: formData?.email ? formData?.email : "", + + tenantCohortRoleMapping: [ + { + roleId: cohortAdminRole.roleId, + tenantId: selectedRowData?.tenantId, + cohortId: [selectedRowData?.cohortId], + }, + ], + }; + const resp = await userCreate(obj as any, selectedRowData?.tenantId); + + if (resp?.responseCode === 200 || resp?.responseCode === 201) { + showToastMessage( + t("COHORTS.COHORT_ADMIN_CREATE_SUCCESSFULLY"), + "success" + ); + setLoading(false); + } else { + showToastMessage(t("COHORTS.COHORT_ADMIN_CREATE_FAILED"), "error"); + } + } catch (error) { + console.error("Error updating cohort:", error); + showToastMessage(t("COHORTS.CREATE_FAILED"), "error"); + } finally { + setLoading(false); + setConfirmButtonDisable(false); + handleCloseModal(); + onCloseEditMOdel(); + fetchUserList(); + setIsEditForm(false); + } + }; - // props to send in header const userProps = { + tenants: listOfTenants, userType: t("COHORTS.COHORTS"), searchPlaceHolder: t("COHORTS.SEARCH_COHORT"), showTenantCohortDropDown: true, isTenantShow: true, - selectedState: selectedState, + // selectedState: selectedState, selectedStateCode: selectedStateCode, selectedDistrict: selectedDistrict, // selectedDistrictCode: selectedDistrictCode, @@ -1115,6 +1297,7 @@ const Center: React.FC = () => { selectedFilter: selectedFilter, statusArchived: true, statusInactive: true, + selectedTenant: selectedTenant, handleTenantChange: handleTenantChange, // handleStateChange: handleStateChange, handleDistrictChange: handleDistrictChange, @@ -1173,11 +1356,13 @@ const Center: React.FC = () => { ) : cohortData?.length > 0 ? ( Numbers.TEN} PagesSelector={PagesSelector} pagination={pagination} @@ -1198,7 +1383,7 @@ const Center: React.FC = () => { height="20vh" > - {t("COMMON.NO_CENTER_FOUND")} + {t("COMMON.NO_COHORT_FOUND")} )} @@ -1275,6 +1460,7 @@ const Center: React.FC = () => { )} + { )} + + + {cohortAdminSchema && cohortAdminUiSchema && ( + + + + + + + )} + ); diff --git a/src/pages/login.tsx b/src/pages/login.tsx index bd7ca818..4546ca7a 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -50,6 +50,7 @@ const LoginPage = () => { const [lang, setLang] = useState(""); const [selectedLanguage, setSelectedLanguage] = useState(lang); const [language, setLanguage] = useState(selectedLanguage); + const [adminInfo, setAdminInfo] = useState(); const theme = useTheme(); const router = useRouter(); @@ -136,17 +137,19 @@ const LoginPage = () => { } const fieldValue = true; if (userId) { - console.log("true"); const response = await getUserDetailsInfo(userId, fieldValue); const userInfo = response?.userData; + setAdminInfo(userInfo); + const tenantId = userInfo?.tenantData?.[0]?.tenantId; + localStorage.setItem("tenantId", tenantId); //set user info in zustand store if (typeof window !== "undefined" && window.localStorage) { localStorage.setItem("adminInfo", JSON.stringify(userInfo)); localStorage.setItem("stateName", userInfo?.customFields[0]?.value); } - if (userInfo.role !== Role.ADMIN) { + if (userInfo.tenantData?.[0]?.roleName === Role.LOGIN_LEARNER) { const errorMessage = t("LOGIN_PAGE.ACCESS_DENIED"); showToastMessage(errorMessage, "error"); localStorage.removeItem("token"); @@ -185,7 +188,6 @@ const LoginPage = () => { const userResponse = await getUserId(); localStorage.setItem("userId", userResponse?.userId); - // Update Zustand store setUserId(userResponse?.userId || ""); localStorage.setItem("name", userResponse?.name); @@ -391,7 +393,7 @@ const LoginPage = () => { marginTop="1.2rem" className="remember-me-checkbox" > - {/* setRememberMe(e.target.checked)} checked={rememberMe} /> @@ -414,7 +416,7 @@ const LoginPage = () => { }} > {t("LOGIN_PAGE.REMEMBER_ME")} - */} +