diff --git a/public/locales/en/common.json b/public/locales/en/common.json index e8ad269a..e66ff50f 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -63,6 +63,9 @@ "USERS_EMAIL": "Users Email", "STATE_NAME_REQUIRED": "State Name is Required", "DISTRICT_NAME_REQUIRED": "District Name is Required", + + "ADD":"Add", + "INACTIVE":"Inactive", "SELECT_STATE": "Select State", "SOMETHING_WENT_WRONG": "Something went wrong" }, @@ -265,7 +268,7 @@ "ENTER_ONLY_DIGITS": "Please enter only digits", "MIN_LENGTH_DIGITS_ERROR": "Minimum {{minLength}} Digits required", "MAX_LENGTH_DIGITS_ERROR": "Maximum {{maxLength}} Digits allowed", - "MIN_LENGTH_CHARACTERS_ERROR": "Minimum {{minLength}} characters required", + "MIN_LENGTH_CHARACTERS_ERROR": "Minimum {{minLength}} characters required", "MAX_LENGTH_CHARACTERS_ERROR": "Maximum {{maxLength}} characters allowed", "NUMBER_AND_SPECIAL_CHARACTERS_NOT_ALLOWED": "Numbers and special characters are not allowed", "CHARACTERS_AND_SPECIAL_CHARACTERS_NOT_ALLOWED": "Characters and special characters are not allowed", diff --git a/src/components/AreaSelection.tsx b/src/components/AreaSelection.tsx index f0163a10..c20045ba 100644 --- a/src/components/AreaSelection.tsx +++ b/src/components/AreaSelection.tsx @@ -3,6 +3,7 @@ import { useTheme } from "@mui/material/styles"; import { useTranslation } from "next-i18next"; import React from "react"; import MultipleSelectCheckmarks from "./FormControl"; +import { capitalizeFirstLetterOfEachWordInArray} from "@/utils/Helper"; interface State { value: string; label: string; @@ -125,7 +126,7 @@ const AreaSelection: React.FC = ({ {isCenterSelection && ( centers.name)} + names={capitalizeFirstLetterOfEachWordInArray(allCenters?.map((centers) => centers.name))} codes={allCenters?.map((centers) => centers.cohortId)} tagName={t("CENTERS.CENTERS")} selectedCategories={selectedCenter} diff --git a/src/components/CommonUserModal.tsx b/src/components/CommonUserModal.tsx index 6e18a9c1..c4ce428b 100644 --- a/src/components/CommonUserModal.tsx +++ b/src/components/CommonUserModal.tsx @@ -30,7 +30,7 @@ import { showToastMessage } from "./Toastify"; import { transformArray } from "../utils/Helper"; import { useLocationState } from "@/utils/useLocationState"; import { tenantId } from "../../app.config"; - +import useSubmittedButtonStore from "@/utils/useSharedState"; interface UserModalProps { open: boolean; onClose: () => void; @@ -53,8 +53,12 @@ const CommonUserModal: React.FC = ({ console.log(userType); const [schema, setSchema] = React.useState(); const [uiSchema, setUiSchema] = React.useState(); + const [submitButtonEnable, setSubmitButtonEnable] = React.useState(false); + const { t } = useTranslation(); const [formValue, setFormValue] = useState(); + const setSubmittedButtonStatus = useSubmittedButtonStore((state:any) => state.setSubmittedButtonStatus); + const modalTitle = !isEditModal ? userType === FormContextType.STUDENT ? t("LEARNERS.NEW_LEARNER") @@ -147,8 +151,9 @@ const CommonUserModal: React.FC = ({ data: IChangeEvent, event: React.FormEvent, ) => { + console.log("submitted") // setOpenModal(true); - const target = event.target as HTMLFormElement; + const target = event?.target as HTMLFormElement; // const elementsArray = Array.from(target.elements); console.log("onsubmit", data); @@ -216,25 +221,33 @@ const CommonUserModal: React.FC = ({ fieldSchema?.hasOwnProperty("isDropdown") || fieldSchema?.hasOwnProperty("isCheckbox") ) { - - apiBody.customFields.push({ - fieldId: fieldId, - value: [String(fieldValue)], - }); - - } else { - if (fieldSchema.checkbox && fieldSchema.type === "array" && isEditModal) { apiBody.customFields.push({ fieldId: fieldId, - value: String(fieldValue).split(","), + value: [String(fieldValue)], }); + + } else { + if (fieldSchema?.checkbox && fieldSchema.type === "array" ) { + if(String(fieldValue).length!=0) + { apiBody.customFields.push({ fieldId: fieldId, - value: String(fieldValue), + value: String(fieldValue).split(","), }); } + + } else { + if(fieldId) + { + apiBody.customFields.push({ + fieldId: fieldId, + value: String(fieldValue), + }); + } + + } } } }); @@ -278,6 +291,7 @@ const CommonUserModal: React.FC = ({ showToastMessage(t(messageKey), "success"); } else { const response = await createUser(apiBody); + console.log(response) if (response) { const messageKey = @@ -289,11 +303,15 @@ const CommonUserModal: React.FC = ({ showToastMessage(t(messageKey), "success"); } + else{ + showToastMessage(t('COMMON.SOMETHING_WENT_WRONG'), 'error'); + + } } + onSubmit(true); onClose(); } catch (error) { - showToastMessage(t('COMMON.SOMETHING_WENT_WRONG'), 'error'); onClose(); console.log(error); @@ -377,17 +395,54 @@ const CommonUserModal: React.FC = ({ // console.error('Error creating user', error); // } }; + useEffect(() => { + + if(!open) + { + setSubmitButtonEnable(false) + } + if(dynamicForm && userType!==FormContextType.TEAM_LEADER || isEditModal) + { + setSubmitButtonEnable(true) + } + if(dynamicFormForBlock && userType===FormContextType.TEAM_LEADER || isEditModal) + { + setSubmitButtonEnable(true) + + } + + }, [dynamicForm,dynamicFormForBlock, open]); return ( <> + + + } > - <> - = ({ selectedCenter={selectedCenter} handleCenterChangeWrapper={handleCenterChangeWrapper} /> - - - + ) +} {formData ? schema && uiSchema && ( = ({ customFields={customFields} formData={formData} > + + {/* */} ) - : userType === "TEAM LEADER" + : userType === FormContextType.TEAM_LEADER ? dynamicFormForBlock && schema && uiSchema && ( = ({ showErrorList={true} customFields={customFields} formData={formValue} + > {/* */} @@ -459,6 +520,8 @@ const CommonUserModal: React.FC = ({ schema && uiSchema && ( = ({ showErrorList={true} customFields={customFields} formData={formValue} + > {/* */} )} + ); diff --git a/src/components/DynamicForm.tsx b/src/components/DynamicForm.tsx index 2cdbf727..ae3c94d6 100644 --- a/src/components/DynamicForm.tsx +++ b/src/components/DynamicForm.tsx @@ -3,10 +3,11 @@ import { Theme as MaterialUITheme } from "@rjsf/mui"; import { RJSFSchema, RegistryFieldsType, WidgetProps } from "@rjsf/utils"; import validator from "@rjsf/validator-ajv8"; import { useTranslation } from "next-i18next"; -import React, { ReactNode } from "react"; +import React, { ReactNode, useState , useEffect} from "react"; import CustomRadioWidget from "./CustomRadioWidget"; import MultiSelectCheckboxes from "./MultiSelectCheckboxes"; - +import { Button, Box } from "@mui/material"; +import useSubmittedButtonStore from "@/utils/useSharedState"; const FormWithMaterialUI = withTheme(MaterialUITheme); interface DynamicFormProps { @@ -20,6 +21,8 @@ interface DynamicFormProps { onChange: (event: IChangeEvent) => void; onError: (errors: any) => void; showErrorList: boolean; + id?: string; // Optional id prop + widgets: { [key: string]: React.FC>; @@ -28,9 +31,11 @@ interface DynamicFormProps { [key: string]: React.FC>; }; children?: ReactNode; + } const DynamicForm: React.FC = ({ + id, schema, uiSchema, formData, @@ -39,20 +44,28 @@ const DynamicForm: React.FC = ({ onError, customFields, children, + }) => { - console.log(formData); + const { t } = useTranslation(); + const [localFormData, setLocalFormData] = useState(formData || {}); + const submittedButtonStatus = useSubmittedButtonStore((state: any) => state.submittedButtonStatus); + const setSubmittedButtonStatus = useSubmittedButtonStore((state:any) => state.setSubmittedButtonStatus); + const setSubmittedButtonEnable = useSubmittedButtonStore((state:any) => state.setSubmittedButtonEnable); + const submittedButtonEnable = useSubmittedButtonStore((state: any) => state.submittedButtonEnable); + + + const widgets: any = { MultiSelectCheckboxes: MultiSelectCheckboxes, - CustomRadioWidget: CustomRadioWidget, + CustomRadioWidget: CustomRadioWidget }; - const { t } = useTranslation(); const handleError = (errors: any) => { - console.log("handle error", errors); + console.log("handle error1", errors); if (errors.length > 0) { const property = errors[0].property?.replace(/^root\./, ""); const errorField = document.querySelector( - `[name$="${property}"]`, + `[name$="${property}"]` ) as HTMLElement; if (errorField) { @@ -67,13 +80,49 @@ const DynamicForm: React.FC = ({ onError(errors); }; - function transformErrors(errors: any) { + const handleSubmit = ( + event: IChangeEvent, + formEvent: React.FormEvent + ) => { + console.log("Submit button clicked"); + + onSubmit(event, formEvent); + }; + + const handleChange = (event: IChangeEvent) => { + const cleanAndReplace = (data: any) => { + for (const key in data) { + if (Array.isArray(data[key])) { + data[key] = Array.from( + new Set( + data[key] + .filter((item: any) => item !== "") + //.map((item: any) => (item === "_lifeskills" ? "life_skills" : item)) + ) + ); + } + } + return data; + }; + + const cleanedFormData = cleanAndReplace(event.formData); + console.log("Form data changed:", cleanedFormData); + + setLocalFormData(cleanedFormData); + // Update local form data state + onChange({ ...event, formData: cleanedFormData }); + }; + + const transformErrors = (errors: any) => { console.log("errors", errors); console.log("schema", schema); + //if (!submitted) return []; + return errors?.map((error: any) => { switch (error.name) { case "required": { - error.message = t("FORM_ERROR_MESSAGES.THIS_IS_REQUIRED_FIELD"); + console.log(submittedButtonStatus); + error.message = submittedButtonStatus? t("FORM_ERROR_MESSAGES.THIS_IS_REQUIRED_FIELD") : ""; break; } case "maximum": { @@ -81,41 +130,27 @@ const DynamicForm: React.FC = ({ if (schema.properties?.[property]?.validation?.includes("numeric")) { error.message = t("FORM_ERROR_MESSAGES.MAX_LENGTH_DIGITS_ERROR", { - maxLength: schema.properties?.[property]?.maxLength, + maxLength: schema.properties?.[property]?.maxLength }); } + break; } case "minimum": { const property = error.property.substring(1); if (schema.properties?.[property]?.validation?.includes("numeric")) { error.message = t("FORM_ERROR_MESSAGES.MIN_LENGTH_DIGITS_ERROR", { - minLength: schema.properties?.[property]?.minLength, + minLength: schema.properties?.[property]?.minLength }); } + break; } case "pattern": { - // if (schema.properties?.[property]?.validation?.includes("numeric")) { - // error.message = t("FORM_ERROR_MESSAGES.ENTER_ONLY_DIGITS"); - // } else if ( - // schema.properties?.[property]?.validation?.includes( - // "characters-with-space" - // ) - // ) { - // error.message = t( - // "FORM_ERROR_MESSAGES.NUMBER_AND_SPECIAL_CHARACTERS_NOT_ALLOWED" - // ); - // } else if (error.params.pattern === "^[a-z A-Z]+$") { - // error.message = t( - // "FORM_ERROR_MESSAGES.NUMBER_AND_SPECIAL_CHARACTERS_NOT_ALLOWED" - // ); - // } - const pattern = error?.params?.pattern; console.log(pattern); const property = error.property.substring(1); switch (pattern) { - case '^[a-zA-Z][a-zA-Z ]*[a-zA-Z]$':{ + case '^[a-zA-Z][a-zA-Z ]*[a-zA-Z]$': { error.message = t( "FORM_ERROR_MESSAGES.NUMBER_AND_SPECIAL_CHARACTERS_NOT_ALLOWED", ); @@ -178,21 +213,19 @@ const DynamicForm: React.FC = ({ return error; }); - } - - function handleChange(event: any) { - console.log("Form data changed:", event.formData); - onChange(event); - } + }; + useEffect(() => { + setSubmittedButtonStatus(false) + }, []); return ( -
+
= ({ onError={handleError} transformErrors={transformErrors} fields={customFields} + id={id} > + + {children} +
); diff --git a/src/components/GeneratedSchemas.ts b/src/components/GeneratedSchemas.ts index 2d098653..fdc4b889 100644 --- a/src/components/GeneratedSchemas.ts +++ b/src/components/GeneratedSchemas.ts @@ -21,7 +21,7 @@ export const GenerateSchemaAndUiSchema = ( }; const uiSchema: UiSchema = {}; //form ui schema let formValues: any = {}; - // console.log('FormData', formData) + console.log('FormData', formData) formData?.fields?.forEach((field: Field) => { const { label, @@ -86,9 +86,9 @@ export const GenerateSchemaAndUiSchema = ( oneOf: options?.map((opt: FieldOption) => ({ const: opt.value, title: - t(`FORM.${opt.label}`) === `FORM.${opt.label}` - ? opt.label - : t(`FORM.${opt.label}`), + t(`FORM.${opt.value}`) === `FORM.${opt.value}` + ? opt.value + : t(`FORM.${opt.value}`), })), }; fieldSchema.uniqueItems = true; diff --git a/src/components/HeaderComponent.tsx b/src/components/HeaderComponent.tsx index 30c28172..5133bc97 100644 --- a/src/components/HeaderComponent.tsx +++ b/src/components/HeaderComponent.tsx @@ -32,7 +32,7 @@ interface Block { label: string; } const Sort = ["A-Z", "Z-A"]; -const Filter = ["Active", "Archived"]; +const Filter = ["Active", "InActive"]; const HeaderComponent = ({ children, diff --git a/src/components/KaTableComponent.tsx b/src/components/KaTableComponent.tsx index 9ac9abbe..34e9f227 100644 --- a/src/components/KaTableComponent.tsx +++ b/src/components/KaTableComponent.tsx @@ -127,7 +127,7 @@ const KaTableComponent: React.FC = ({ if (props.rowData?.status === Status.ARCHIVED) { return ( { onChange={handleChange} > - None + {/* None */} {options?.map((option: any) => ( diff --git a/src/components/SimpleModal.tsx b/src/components/SimpleModal.tsx index 34e3cf85..7b9d7719 100644 --- a/src/components/SimpleModal.tsx +++ b/src/components/SimpleModal.tsx @@ -1,9 +1,6 @@ import { useMediaQuery, useTheme, Box, Button, Divider, Modal, Typography } from "@mui/material"; import React, { ReactNode } from "react"; - -import manageUserStore from "@/store/manageUserStore"; import CloseSharpIcon from "@mui/icons-material/CloseSharp"; -import { useTranslation } from "next-i18next"; interface SimpleModalProps { secondaryActionHandler?: () => void; @@ -11,11 +8,13 @@ interface SimpleModalProps { secondaryText?: string; primaryText?: string; showFooter?: boolean; + footer?: ReactNode; children: ReactNode; open: boolean; onClose: () => void; modalTitle: string; } + const SimpleModal: React.FC = ({ open, onClose, @@ -24,34 +23,30 @@ const SimpleModal: React.FC = ({ showFooter = true, primaryActionHandler, secondaryActionHandler, + footer, children, modalTitle, }) => { const theme = useTheme(); const isSmallScreen = useMediaQuery(theme.breakpoints.down("sm")); - const isMediumScreen = useMediaQuery(theme.breakpoints.between("sm", "md")); - const isLargeScreen = useMediaQuery(theme.breakpoints.up("lg")); const modalStyle = { - paddingTop: "0", - paddingLeft: theme.spacing(2), - paddingRight: theme.spacing(2), - paddingBottom: theme.spacing(2), - position: "absolute", + display: "flex", + flexDirection: "column" as const, + position: "absolute" as const, top: "50%", left: "50%", transform: "translate(-50%, -50%)", width: isSmallScreen ? "90%" : isLargeScreen ? "65%" : "85%", maxHeight: "80vh", - overflowY: "auto", backgroundColor: "#fff", borderRadius: theme.shape.borderRadius, boxShadow: theme.shadows[5], }; const titleStyle = { - position: "sticky", + position: "sticky" as const, top: "0", backgroundColor: "#fff", padding: theme.spacing(2), @@ -59,82 +54,90 @@ const SimpleModal: React.FC = ({ borderBottom: `1px solid ${theme.palette.divider}`, }; + const contentStyle = { + flex: 1, + overflowY: "auto" as const, + padding: theme.spacing(2), + }; + + const footerStyle = { + padding: theme.spacing(2), + borderTop: `1px solid ${theme.palette.divider}`, + }; + return ( - - - - {modalTitle} - - - - - + + + {modalTitle} + + - {children} + {children} - - {showFooter ? ( - - {primaryText && ( - - )} - - {secondaryText && ( - - )} - - ) : null} + {showFooter && ( + footer ? ( + {footer} // Render the custom footer content if provided + ) : ( + + {primaryText && ( + + )} + {secondaryText && ( + + )} + + ) + )} ); diff --git a/src/components/UserTable.tsx b/src/components/UserTable.tsx index 0e8cfa29..ea970444 100644 --- a/src/components/UserTable.tsx +++ b/src/components/UserTable.tsx @@ -1,7 +1,7 @@ import DeleteUserModal from "@/components/DeleteUserModal"; import HeaderComponent from "@/components/HeaderComponent"; import PageSizeSelector from "@/components/PageSelector"; -import { SORT, Status } from "@/utils/app.constant"; +import { FormContextType, SORT, Status } from "@/utils/app.constant"; import DeleteIcon from "@mui/icons-material/Delete"; import EditIcon from "@mui/icons-material/Edit"; import Box from "@mui/material/Box"; @@ -46,6 +46,7 @@ type FilterDetails = { districts?: any; states?: any; blocks?: any; + name?:any }; interface Cohort { @@ -91,12 +92,13 @@ const UserTable: React.FC = ({ const [selectedUserId, setSelectedUserId] = useState(""); const [selectedReason, setSelectedReason] = useState(""); const [otherReason, setOtherReason] = useState(""); - const isMobile = useMediaQuery((theme: Theme) => - theme.breakpoints.down("sm"), - ); - // const isMobile = useMediaQuery("(max-width:600px)"); + const [deleteUserState, setDeleteUserState] = useState(false); + + const isMobile: boolean = useMediaQuery((theme: Theme) => + theme.breakpoints.down('sm'), +); - const [confirmButtonDisable, setConfirmButtonDisable] = useState(false); + const [confirmButtonDisable, setConfirmButtonDisable] = useState(true); const [pagination, setPagination] = useState(true); const [selectedFilter, setSelectedFilter] = useState("All"); const [formdata, setFormData] = useState(); @@ -110,7 +112,8 @@ const UserTable: React.FC = ({ setOpenAddLearnerModal(true); }; const handleModalSubmit = (value: boolean) => { - setSubmitValue(true); + console.log("true") + submitValue?setSubmitValue(false):setSubmitValue(true); }; const handleCloseAddLearnerModal = () => { setOpenAddLearnerModal(false); @@ -160,6 +163,7 @@ const UserTable: React.FC = ({ count={pageCount} page={pageOffset + 1} onChange={handlePaginationChange} + sx={{marginTop:"10px"}} /> ); @@ -196,13 +200,13 @@ const UserTable: React.FC = ({ const handleFilterChange = async (event: SelectChangeEvent) => { console.log(event.target.value as string); setSelectedFilter(event.target.value as string); - if (event.target.value === "Active") { + if (event.target.value === Status.ACTIVE_LABEL ) { console.log(true); setFilters((prevFilters) => ({ ...prevFilters, status: [Status.ACTIVE], })); - } else if (event.target.value === "Archived") { + } else if (event.target.value === Status.INACTIVE) { setFilters((prevFilters) => ({ ...prevFilters, status: [Status.ARCHIVED], @@ -376,6 +380,10 @@ const UserTable: React.FC = ({ return initialFormData; }; const handleEdit = async (rowData: any) => { + if(submitValue) + { + setSubmitValue(false); + } console.log("Edit row:", rowData); try { @@ -437,8 +445,12 @@ const UserTable: React.FC = ({ const result = resp?.getUserDetails; // console.log(resp?.totalCount) if (resp?.totalCount >= 15) { + setPagination(true); + setPageSizeArray([5, 10, 15]); } else if (resp?.totalCount >= 10) { + setPagination(true); + // setPageSize(resp?.totalCount); setPageSizeArray([5, 10]); } else if (resp?.totalCount > 5) { @@ -479,20 +491,38 @@ const UserTable: React.FC = ({ user.name.slice(1).toLowerCase(), role: user.role, // gender: user.gender, - mobile: user.mobile === "NaN" ? "" : user.mobile, - age: ageField ? ageField.value : null, - district: districtField ? districtField.value : null, - state: stateField ? stateField.value : null, - blocks: blockField ? blockField.value : null, + mobile: user.mobile === "NaN" ? "-" : user.mobile, + age: ageField ? ageField.value : "-", + district: districtField ? districtField.value : "-", + state: stateField ? stateField.value : "-", + blocks: blockField ? blockField.value : "-", gender: genderField ? genderField.value?.charAt(0)?.toUpperCase() + genderField.value.slice(1).toLowerCase() - : null, + : "-", // centers: null, // Programs: null, }; }); - setData(finalResult); + if(filters?.name) + { + const prioritizedResult = finalResult.sort((a: any, b: any) => { + const aStartsWith = a.name.toLowerCase().startsWith(filters?.name); + const bStartsWith = b.name.toLowerCase().startsWith(filters?.name); + + if (aStartsWith && !bStartsWith) return -1; + if (!aStartsWith && bStartsWith) return 1; + return 0; + }); + + setData(prioritizedResult); + } + + else{ + setData(finalResult); + + } + setLoading(false); setCohortsFetched(false); } catch (error: any) { @@ -513,8 +543,9 @@ const UserTable: React.FC = ({ pageLimit, sortBy, filters, - openAddFacilitatorModal, - openAddTeamLeaderModal, + parentState, + deleteUserState + ]); useEffect(() => { @@ -526,9 +557,15 @@ const UserTable: React.FC = ({ const newData = await Promise.all( data?.map(async (user) => { const response = await getCohortList(user.userId); - const cohortNames = response?.result?.cohortData?.map( - (cohort: Cohort) => cohort.name, - ); + // const cohortNames = response?.result?.cohortData?.map( + // (cohort: Cohort) => cohort.name, + // ); + const cohortNames = response?.result?.cohortData + ?.filter((cohort: Cohort) => cohort.type !== 'BLOCK') // Filter out cohorts with type 'block' + .map((cohort: Cohort) => cohort.name); // + + + let finalArray; if (cohortNames?.length >= 1) { finalArray = capitalizeFirstLetterOfEachWordInArray(cohortNames); @@ -537,7 +574,7 @@ const UserTable: React.FC = ({ // console.log(finalArray) return { ...user, - centers: finalArray?.join(" , "), + centers: finalArray? finalArray?.join(" , "):"-", }; }), ); @@ -554,6 +591,7 @@ const UserTable: React.FC = ({ setSelectedReason(""); setOtherReason(""); setIsDeleteModalOpen(false); + setConfirmButtonDisable(true); }; const handleDeleteUser = async (category: string) => { @@ -567,6 +605,10 @@ const UserTable: React.FC = ({ }, }; const response = await deleteUser(userId, userData); + if(response) + { + deleteUserState?setDeleteUserState(false):setDeleteUserState(true) + } handleCloseDeleteModal(); showToastMessage(t("COMMON.USER_DELETE_SUCCSSFULLY"), "success"); } catch (error) { @@ -578,7 +620,15 @@ const UserTable: React.FC = ({ { name: "Edit", onClick: handleEdit, icon: EditIcon }, { name: "Delete", onClick: handleDelete, icon: DeleteIcon }, ]; - + const noUserFoundJSX = ( + + {/* */} + + + {t("COMMON.NO_USER_FOUND")} + + + ); const userProps = { userType: userType, searchPlaceHolder: searchPlaceholder, @@ -606,7 +656,7 @@ const UserTable: React.FC = ({ ) : data.length !== 0 && loading === false ? ( = ({ onEdit={handleEdit} onDelete={handleDelete} pagination={pagination} + noDataMessage={ + data.length === 0 ? t("COMMON.NO_USER_FOUND") : "" + } /> ) : ( loading === false && data.length === 0 && ( - - {/* */} - - - {t("COMMON.NO_USER_FOUND")} - - - ) - )} - - {/* + // {/* */} + // + // + // {t("COMMON.NO_USER_FOUND")} + // + // + + = ({ showIcons={true} onEdit={handleEdit} onDelete={handleDelete} - - /> */} + pagination={false} + noDataMessage={ + data.length === 0 ? noUserFoundJSX : "" + } + /> + + ) + + )} + + = ({ userId={userId} onSubmit={handleModalSubmit} userType={ - userType === "Learners" - ? "STUDENT" - : userType === "Facilitators" - ? "TEACHER" - : "TEAM LEADER" + userType === Role.LEARNERS + ? FormContextType.STUDENT + : userType === Role.FACILITATORS + ? FormContextType.TEACHER + : FormContextType.TEAM_LEADER } /> diff --git a/src/components/layouts/header/Header.tsx b/src/components/layouts/header/Header.tsx index 9230cacb..05ccf374 100644 --- a/src/components/layouts/header/Header.tsx +++ b/src/components/layouts/header/Header.tsx @@ -1,10 +1,23 @@ -import React from "react"; +import React, { useEffect, useRef, useState } from "react"; import FeatherIcon from "feather-icons-react"; import { AppBar, Box, IconButton, Toolbar } from "@mui/material"; +import MenuItem from "@mui/material/MenuItem"; +import Select, { SelectChangeEvent } from "@mui/material/Select"; +import config from "../../../../config.json"; import PropTypes from "prop-types"; // Dropdown Component import SearchBar from "./SearchBar"; +import { useRouter } from 'next/router'; +import { + Button, + FormControl, + TextField, + Grid, + Typography, + useMediaQuery, + // Import useMediaQuery hook +} from "@mui/material"; import { useTranslation } from "next-i18next"; import { createTheme } from "@mui/material/styles"; import Profile from "./Profile"; @@ -12,6 +25,28 @@ import Profile from "./Profile"; const Header = ({ sx, customClass, toggleMobileSidebar, position }: any) => { const { t } = useTranslation(); const theme = createTheme(); + const [lang, setLang] = useState(""); + const router = useRouter(); + + const [selectedLanguage, setSelectedLanguage] = useState(lang); + + const [language, setLanguage] = useState(selectedLanguage); + useEffect(() => { + if (typeof window !== 'undefined' && window.localStorage) { + const lang = localStorage.getItem('preferredLanguage') || 'en'; + setLanguage(lang); + + } + }, [setLanguage]); + + const handleChange = (event: SelectChangeEvent) => { + const newLocale = event.target.value; + setLanguage(newLocale); + if (typeof window !== 'undefined' && window.localStorage) { + localStorage.setItem('preferredLanguage', newLocale); + router.replace(router.pathname, router.asPath, { locale: newLocale }); + } + }; return ( @@ -41,7 +76,39 @@ const Header = ({ sx, customClass, toggleMobileSidebar, position }: any) => { + + + + + + {/* ------------------------------------------- */} {/* Profile Dropdown */} {/* ------------------------------------------- */} diff --git a/src/components/layouts/header/SearchBar.tsx b/src/components/layouts/header/SearchBar.tsx index 92228c0f..7c2c1d5b 100644 --- a/src/components/layouts/header/SearchBar.tsx +++ b/src/components/layouts/header/SearchBar.tsx @@ -1,7 +1,8 @@ import SearchIcon from "@mui/icons-material/Search"; +import CloseIcon from "@mui/icons-material/Close"; import { IconButton, InputBase, Paper, useMediaQuery } from "@mui/material"; import { styled } from "@mui/system"; -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import { useTranslation } from "react-i18next"; interface SearchBarProps { @@ -33,21 +34,31 @@ const SearchBar: React.FC = ({ onSearch, placeholder }) => { theme.breakpoints.down("sm"), ); + const validateKeyword = (keyword: string) => { + return /^(\S+)( \S+)*$/.test(keyword.trim()) && !/\s{2,}/.test(keyword); + }; + + useEffect(() => { + if (keyword.trim().length >= 3 && validateKeyword(keyword)) { + onSearch(keyword); + } + }, [keyword]); + const handleInputChange = (event: React.ChangeEvent) => { - if (event.target.value === "") { + const value = event.target.value; + // Replace multiple spaces with a single space + const normalizedValue = value.replace(/\s{2,}/g, ' ').trimStart(); + setKeyword(normalizedValue); + // setKeyword(value.trimStart()); + + if (value === "") { onSearch(""); } - setKeyword(event.target.value); - }; - - const handleSearch = () => { - onSearch(keyword); }; - const handleKeyPress = (event: React.KeyboardEvent) => { - if (event.key === "Enter") { - handleSearch(); - } + const handleClear = () => { + setKeyword(""); + onSearch(""); }; return ( @@ -56,10 +67,22 @@ const SearchBar: React.FC = ({ onSearch, placeholder }) => { placeholder={placeholder} value={keyword} onChange={handleInputChange} - onKeyPress={handleKeyPress} inputProps={{ "aria-label": "search" }} /> - + {keyword && ( + + + + )} + onSearch(keyword)} + disabled={keyword.trim().length < 3} + > diff --git a/src/data/tableColumns.ts b/src/data/tableColumns.ts index e54d4ebc..024e6fcb 100644 --- a/src/data/tableColumns.ts +++ b/src/data/tableColumns.ts @@ -7,47 +7,47 @@ export const getUserTableColumns = (t: any, isMobile: any) => { title: t("FORM.NAME"), dataType: DataType.String, // sortDirection: SortDirection.Ascend, - width: isMobile ? 160 : null, + width: isMobile ? 160 : undefined, }, { key: "status", title: t("FORM.STATUS"), dataType: DataType.String, /// sortDirection: SortDirection.Ascend, - width: isMobile ? 160 : null, + width: isMobile ? 160 : undefined, }, { key: "age", title: t("FORM.AGE"), dataType: DataType.String, - width: isMobile ? 160 : null, + width: isMobile ? 160 : undefined, }, { key: "gender", title: t("FORM.GENDER"), dataType: DataType.String, - width: isMobile ? 160 : null, + width: isMobile ? 160 : undefined, }, { key: "mobile", title: t("FORM.MOBILE_NUMBER"), dataType: DataType.String, - width: isMobile ? 160 : null, + width: isMobile ? 160 : undefined, }, { key: "state", title: t("FORM.STATE"), dataType: DataType.String, sortDirection: SortDirection.Ascend, - width: isMobile ? 160 : null, + width: isMobile ? 160 : undefined, }, { key: "district", title: t("FORM.DISTRICT"), dataType: DataType.String, sortDirection: SortDirection.Ascend, - width: isMobile ? 160 : null, + width: isMobile ? 160 : undefined, }, { @@ -55,20 +55,20 @@ export const getUserTableColumns = (t: any, isMobile: any) => { title: t("FORM.BLOCK"), dataType: DataType.String, sortDirection: SortDirection.Ascend, - width: isMobile ? 160 : null, + width: isMobile ? 160 : undefined, }, { key: "centers", title: t("FORM.CENTER"), dataType: DataType.String, sortDirection: SortDirection.Ascend, - width: isMobile ? 160 : null, + width: isMobile ? 160 : undefined, }, { key: "actions", title: t("FORM.ACTION"), dataType: DataType.String, - width: isMobile ? 160 : null, + width: isMobile ? 160 : undefined, }, ]; }; @@ -80,27 +80,27 @@ export const getTLTableColumns = (t: any, isMobile: any) => { title: t("FORM.NAME"), dataType: DataType.String, //sortDirection: SortDirection.Ascend, - // width: isMobile?160:null, + width: isMobile?160:undefined, }, { key: "status", title: t("FORM.STATUS"), dataType: DataType.String, // sortDirection: SortDirection.Ascend, - // width: isMobile?160:null, - }, + width: isMobile?160:undefined, + }, { key: "age", title: t("FORM.AGE"), dataType: DataType.String, - // width: isMobile?160:null, + width: isMobile?160:undefined, }, { key: "gender", title: t("FORM.GENDER"), dataType: DataType.String, - // width: isMobile?160:null, + width: isMobile?160:undefined, }, { @@ -108,14 +108,14 @@ export const getTLTableColumns = (t: any, isMobile: any) => { title: t("FORM.STATE"), dataType: DataType.String, sortDirection: SortDirection.Ascend, - // width: isMobile?160:null, + width: isMobile?160:undefined, }, { key: "district", title: t("FORM.DISTRICT"), dataType: DataType.String, sortDirection: SortDirection.Ascend, - // width: isMobile?160:null, + width: isMobile?160:undefined, }, { @@ -123,14 +123,14 @@ export const getTLTableColumns = (t: any, isMobile: any) => { title: t("FORM.BLOCK"), dataType: DataType.String, // sortDirection: SortDirection.Ascend, - // width: isMobile?160:null, + width: isMobile?160:undefined, }, { key: "actions", title: t("FORM.ACTION"), dataType: DataType.String, - // width: isMobile?160:null, + width: isMobile?160:undefined, }, ]; }; diff --git a/src/pages/centers.tsx b/src/pages/centers.tsx index b7006506..05734464 100644 --- a/src/pages/centers.tsx +++ b/src/pages/centers.tsx @@ -25,7 +25,7 @@ import { showToastMessage } from "@/components/Toastify"; import AddNewCenters from "@/components/AddNewCenters"; import { getCenterTableData } from "@/data/tableColumns"; import { Theme } from "@mui/system"; -import { debounce, firstLetterInUpperCase } from "@/utils/Helper"; +import { firstLetterInUpperCase } from "@/utils/Helper"; type cohortFilterDetails = { type?: string; diff --git a/src/styles/globals.css b/src/styles/globals.css index 5bb5ff4e..09615069 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -53,3 +53,6 @@ input[type="number"]::-webkit-outer-spin-button { margin: 0; appearance: none; } +.rjsf-default-submit { + display: none !important; +} \ No newline at end of file diff --git a/src/utils/app.constant.ts b/src/utils/app.constant.ts index 0db05f7c..234cd0e7 100644 --- a/src/utils/app.constant.ts +++ b/src/utils/app.constant.ts @@ -3,6 +3,8 @@ export enum Role { TEACHER = "Teacher", TEAM_LEADER = "Team Leader", ADMIN = "Admin", + LEARNERS="Learners", + FACILITATORS="Facilitators" } export enum Status { @@ -11,6 +13,7 @@ export enum Status { ACTIVE = "active", ACTIVE_LABEL = "Active", ALL_LABEL = "All", + INACTIVE="Inactive" } export enum SORT { ASCENDING = "asc", diff --git a/src/utils/useLocationState.tsx b/src/utils/useLocationState.tsx index 6982fa84..68074468 100644 --- a/src/utils/useLocationState.tsx +++ b/src/utils/useLocationState.tsx @@ -124,7 +124,7 @@ export const useLocationState = (open: boolean, onClose: () => void) => { // const result = response?.result?.cohortDetails; const dataArray = response?.result?.results?.cohortDetails; - const cohortInfo = dataArray?.map((item: any) => ({ + const cohortInfo = dataArray?.filter((cohort: any) => cohort.type !== 'BLOCK').map((item: any) => ({ cohortId: item?.cohortId, name: item?.name, })); diff --git a/src/utils/useSharedState.ts b/src/utils/useSharedState.ts new file mode 100644 index 00000000..153234f9 --- /dev/null +++ b/src/utils/useSharedState.ts @@ -0,0 +1,11 @@ +import { create } from 'zustand'; + +const useSubmittedButtonStore = create((set) => ({ + submittedButtonStatus: false, + submittedButtonEnable: false, + + setSubmittedButtonStatus: (status: boolean) => set({ submittedButtonStatus: status }), + setSubmittedButtonEnable: (status: boolean) => set({ submittedButtonEnable: status }), +})); + +export default useSubmittedButtonStore;