Skip to content
Closed
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b0372ed
app config console commit
NabeelAyubee Sep 29, 2025
6ee037e
Merge branch 'console' into new-app-config-console
NabeelAyubee Sep 29, 2025
a0dcfb4
clean up 1.0
NabeelAyubee Sep 29, 2025
5b4ad14
clean up 2.0
NabeelAyubee Sep 30, 2025
8066433
clean up 3.0
NabeelAyubee Sep 30, 2025
287f097
clean up 4.0
NabeelAyubee Sep 30, 2025
1e9e145
clean up 6.0
NabeelAyubee Sep 30, 2025
89c3225
Merge branch 'console' into new-app-config-console
nabeelmd-eGov Sep 30, 2025
e5281f0
clearing consoles 7.0
NabeelAyubee Sep 30, 2025
e4ad1a9
clean up
NabeelAyubee Sep 30, 2025
ddce73b
clean up
NabeelAyubee Sep 30, 2025
034cb02
clean up
NabeelAyubee Sep 30, 2025
b7dbb5b
performance: Improve commit message readability
NabeelAyubee Sep 30, 2025
6812f30
integrate flows, roles, template
NabeelAyubee Oct 6, 2025
7530a16
added debounce
NabeelAyubee Oct 6, 2025
8096660
integrate fieldPanelPropertiesSlice
NabeelAyubee Oct 7, 2025
1dc2c85
added integration with mdms
NabeelAyubee Oct 9, 2025
01d410d
Layout Renderer fixes
Ramkrishna-egov Oct 10, 2025
24dff66
added template integration & ui ux fix
NabeelAyubee Oct 14, 2025
0e26590
integrated mdms config
NabeelAyubee Oct 17, 2025
6b725cf
Dependent Field Changes (#3269)
Ramkrishna-egov Oct 22, 2025
5748f60
mdms integrated flow and page
NabeelAyubee Oct 27, 2025
317d116
page fix for forms
NabeelAyubee Oct 27, 2025
1ea0976
update localisation
NabeelAyubee Oct 27, 2025
d701b5b
tranform integration
NabeelAyubee Oct 27, 2025
6b765e0
Fixed Layout renderer for Template
Ramkrishna-egov Oct 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Loader, lazyWithFallback } from "@egovernments/digit-ui-components";
import React from "react";
// import { useRouteMatch } from "react-router-dom";


import { CustomisedHooks } from "./hooks";
import { UICustomizations } from "./configs/UICustomizations";
import CampaignCard from "./components/CampaignCard";
Expand Down Expand Up @@ -65,6 +64,15 @@ import AppConfigurationTabLayer from "./pages/employee/appConfigurationRedesign/
import QRButton from "./components/CreateCampaignComponents/QRButton";
import EqualHeightWrapper from "./components/CreateCampaignComponents/WrapperModuleCard";
import CampaignNameInfo from "./components/CreateCampaignComponents/CampaignNameInfo";
import ButtonTemplate from "./pages/employee/NewAppConfiguration/components/ButtonTemplate";
import CardTemplate from "./pages/employee/NewAppConfiguration/components/CardTemplate";
import Filter from "./pages/employee/NewAppConfiguration/components/Filter";
import InfoCardTemplate from "./pages/employee/NewAppConfiguration/components/InfoCardTemplate";
import PanelCardTemplate from "./pages/employee/NewAppConfiguration/components/PanelCardTemplate";
import SearchBar from "./pages/employee/NewAppConfiguration/components/SearchBar";
import SwitchTemplate from "./pages/employee/NewAppConfiguration/components/SwitchTemplate";
import TagTemplate from "./pages/employee/NewAppConfiguration/components/TagTemplate";

/**
* MDMS Module name
*/
Expand Down Expand Up @@ -126,7 +134,12 @@ const CampaignModule = React.memo(({ stateCode, userType, tenants }) => {
return (
<ErrorBoundary moduleName="CAMPAIGN">
<TourProvider>
<EmployeeApp BOUNDARY_HIERARCHY_TYPE={BOUNDARY_HIERARCHY_TYPE} stateCode={stateCode} userType={userType} hierarchyData={hierarchyData} />
<EmployeeApp
BOUNDARY_HIERARCHY_TYPE={BOUNDARY_HIERARCHY_TYPE}
stateCode={stateCode}
userType={userType}
hierarchyData={hierarchyData}
/>
</TourProvider>
</ErrorBoundary>
);
Expand Down Expand Up @@ -190,6 +203,16 @@ const componentsToRegister = {
QRButton,
EqualHeightWrapper,
CampaignNameInfo,
SearchByProximity: SwitchTemplate,
SearchByID: SwitchTemplate,
ButtonTemplate,
CardTemplate,
Filter,
InfoCardTemplate,
PanelCardTemplate,
SearchBar,
SwitchTemplate,
TagTemplate,
};

const overrideHooks = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,280 +1,63 @@
import React, { Fragment } from "react";
import { Card, CardText, TextInput, SelectionTag, Dropdown, CardHeader, Button, FieldV1, Loader, CheckBox } from "@egovernments/digit-ui-components";
import { DynamicImageComponent } from "./DynamicImageComponent";
import "../utils/template_components/RegistrationComponents";
import { Card, CardText, CardHeader, Button } from "@egovernments/digit-ui-components";
import MobileBezelFrame from "./MobileBezelFrame";
import GenericTemplateScreen from "./GenericTemplateScreen";
import DynamicSVG from "./DynamicSVGComponent";
import RenderSelectionField from "./RenderSelectionField";
import ComponentToRender from "./ComponentToRender";

const MdmsDropdown = ({
t,
moduleMaster,
optionKey = "code",
moduleName,
masterName,
className,
style = {},
variant = "",
selected,
select = () => {},
rest,
}) => {
if (!moduleName || !masterName) return null;
const { isLoading, data } = Digit.Hooks.useCustomMDMS(
Digit.ULBService.getCurrentTenantId(),
moduleName,
[{ name: masterName }],
{
enabled: moduleName && masterName,
select: (data) => {
return data?.[moduleName]?.[masterName]?.filter((item) => item.active);
},
},
{ schemaCode: "MDMSDROPDOWNLIST" } //mdmsv2
);

if (isLoading) return <div>Loading...</div>;
return (
<Dropdown
className={className}
style={style}
variant={variant}
t={t}
option={data}
optionKey={optionKey}
selected={selected}
select={() => select()}
/>
);
};

const renderField = (field, t) => {
switch (field.type) {
case "text":
case "textInput":
return <TextInput name="name" value={field?.name || ""} onChange={() => {}} disabled={true} />;
case "number":
return <TextInput type="number" className="appConfigLabelField-Input" name={""} value={field?.value} onChange={() => {}} />;
case "textarea":
return <TextInput type="textarea" className="appConfigLabelField-Input" name={""} value={field?.value} onChange={() => {}} />;
case "time":
return <TextInput type="time" className="appConfigLabelField-Input" name={""} value={field?.value} onChange={() => {}} />;
case "mobileNumber":
return (
<TextInput
type="text"
className="appConfigLabelField-Input"
name={""}
value={field?.value}
onChange={(event) => onChange(event)}
populators={{ prefix: rest?.countryPrefix }}
/>
);
case "selection":
return <RenderSelectionField field={field} t={t} />;
case "numeric":
case "counter":
return <TextInput name="numeric" onChange={() => {}} type={"numeric"} />;
case "dropdown":
return (
<Dropdown
option={field?.dropDownOptions || []}
optionKey={"name"}
selected={[]}
select={() => {}}
t={t} // disabled={source === "microplan"}
/>
);

case "MdmsDropdown":
return (
<MdmsDropdown
className="appConfigLabelField-Input"
variant={""}
t={t}
option={dropDownOptions}
optionKey={"code"}
selected={null}
select={() => {}}
props={props}
moduleName={rest?.schemaCode ? rest.schemaCode.split(".")[0] : rest?.moduleMaster?.moduleName}
masterName={rest?.schemaCode ? rest.schemaCode.split(".")[1] : rest?.moduleMaster?.masterName}
rest={rest}
/>
);
case "date":
case "dobPicker":
case "datePicker":
case "dob":
return <TextInput type="date" className="appConfigLabelField-Input" name={""} value={field?.value} onChange={() => {}} />;
case "button":
return (
<Button
icon={"QrCodeScanner"}
className="app-preview-field-button"
variation="secondary"
label={t(field?.label)}
title={t(field?.label)}
onClick={() => {}}
/>
);
case "custom":
return <DynamicImageComponent type={field?.type} appType={field?.appType} />;
case "customsvg":
return <DynamicSVG type={field?.type} appType={field?.appType} data={field} />;
default:
return <DynamicImageComponent type={field?.type} appType={field?.appType} />;
}
};

// remove this function
const getFieldType = (field) => {
//TODO Why do we still need this swtich case this should be set as a default supported fields and app field master should help to map this
switch (field.type) {
case "text":
case "textInput":
return "text";
case "number":
return "number";
case "textArea":
return "textarea";
case "time":
return "time";
case "mobileNumber":
return "mobileNumber";
case "checkbox":
return "checkbox";
case "Selection":
case "selection":
case "select":
return "select";
case "numeric":
case "counter":
return "numeric";
case "dropdown":
return "dropdown";
case "MdmsDropdown":
return "custom";
case "date":
case "dobPicker":
case "datePicker":
case "dob":
return "date";
case "radio":
return "radio";
case "custom":
return "custom";
default:
return "button";
}
};
const AppPreview = ({ data = {}, selectedField, t }) => {
const AppPreview = ({ data = {}, selectedField, t, onFieldClick }) => {
return (
<MobileBezelFrame>
{/* <div className="app-preview"> */}
<div className="mobile-bezel-child-container">
{data?.cards?.map((card, index) => (
<Card key={index} className="app-card" style={{}}>
{card.headerFields.map((headerField, headerIndex) => (
<div key={headerIndex}>
{headerField.jsonPath === "ScreenHeading" ? (
<CardHeader>{t(headerField.value)}</CardHeader>
) : (
<CardText className="app-preview-sub-heading">{t(headerField.value)}</CardText>
)}
</div>
))}
{data.type !== "template" &&
card?.fields
?.filter((field) => field.active && (field.hidden == false || field.deleteFlag == true)) //added logic to hide fields in display
?.map((field, fieldIndex) => {
if (getFieldType(field) === "checkbox") {
<Card className="app-card" style={{}}>
{/* RENDERING HEADER AND SUB-HEADING */}
{data.heading && <CardHeader>{t(data.heading)}</CardHeader>}
{data.description && <CardText className="app-preview-sub-heading">{t(data.description)}</CardText>}

{/* RENDERING FORMS */}
{data?.body?.map((card, index) => (
<Fragment key={index}>
{data.type !== "template" &&
card?.fields
?.filter((field) => !field.hidden)
?.map((field, fieldIndex) => {
const isSelected =
selectedField &&
((selectedField.jsonPath && selectedField.jsonPath === field.jsonPath) ||
(selectedField.id && selectedField.id === field.id));

return (
<div
className={`app-preview-field-pair ${
selectedField?.jsonPath && selectedField?.jsonPath === field?.jsonPath
? `app-preview-selected`
: selectedField?.id && selectedField?.id === field?.id
? `app-preview-selected`
: ``
}`}
key={fieldIndex}
onClick={() => onFieldClick && onFieldClick(field, data, card, index, fieldIndex)}
style={{
cursor: "pointer",
border: isSelected ? "2px solid #0B4B66" : "2px solid transparent",
borderRadius: "4px",
padding: "8px",
margin: "4px 0",
backgroundColor: isSelected ? "#f0f8ff" : "transparent",
}}
>
<CheckBox
mainClassName={"app-config-checkbox-main"}
labelClassName={`app-config-checkbox-label ${field?.["toArray.required"] ? "required" : ""}`}
onChange={(e) => {}}
value={""}
label={t(field?.label)}
isLabelFirst={false}
disabled={field?.readOnly || false}
/>
<ComponentToRender field={field} t={t} selectedField={selectedField} />
</div>
);
Comment on lines 28 to 42
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make the field wrapper keyboard-accessible.

This div is clickable but lacks role, tabIndex, and keyboard handlers, triggering the a11y lints. Add role="button", tabIndex={0}, and handle Enter/Space so keyboard users can select fields.

-                      <div
-                        key={fieldIndex}
-                        onClick={() => onFieldClick && onFieldClick(field, data, card, index, fieldIndex)}
+                      <div
+                        key={fieldIndex}
+                        role="button"
+                        tabIndex={0}
+                        aria-label={t(field?.label) || "Select field"}
+                        onClick={() => onFieldClick && onFieldClick(field, data, card, index, fieldIndex)}
+                        onKeyDown={(event) => {
+                          if (event.key === "Enter" || event.key === " ") {
+                            event.preventDefault();
+                            onFieldClick && onFieldClick(field, data, card, index, fieldIndex);
+                          }
+                        }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div
className={`app-preview-field-pair ${
selectedField?.jsonPath && selectedField?.jsonPath === field?.jsonPath
? `app-preview-selected`
: selectedField?.id && selectedField?.id === field?.id
? `app-preview-selected`
: ``
}`}
key={fieldIndex}
onClick={() => onFieldClick && onFieldClick(field, data, card, index, fieldIndex)}
style={{
cursor: "pointer",
border: isSelected ? "2px solid #0B4B66" : "2px solid transparent",
borderRadius: "4px",
padding: "8px",
margin: "4px 0",
backgroundColor: isSelected ? "#f0f8ff" : "transparent",
}}
>
<CheckBox
mainClassName={"app-config-checkbox-main"}
labelClassName={`app-config-checkbox-label ${field?.["toArray.required"] ? "required" : ""}`}
onChange={(e) => {}}
value={""}
label={t(field?.label)}
isLabelFirst={false}
disabled={field?.readOnly || false}
/>
<ComponentToRender field={field} t={t} selectedField={selectedField} />
</div>
);
<div
key={fieldIndex}
role="button"
tabIndex={0}
aria-label={t(field?.label) || "Select field"}
onClick={() => onFieldClick && onFieldClick(field, data, card, index, fieldIndex)}
onKeyDown={(event) => {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
onFieldClick && onFieldClick(field, data, card, index, fieldIndex);
}
}}
style={{
cursor: "pointer",
border: isSelected ? "2px solid #0B4B66" : "2px solid transparent",
borderRadius: "4px",
padding: "8px",
margin: "4px 0",
backgroundColor: isSelected ? "#f0f8ff" : "transparent",
}}
>
<ComponentToRender field={field} t={t} selectedField={selectedField} />
</div>
🧰 Tools
🪛 Biome (2.1.2)

[error] 35-46: Static Elements should not be interactive.

To add interactivity such as a mouse or key event listener to a static element, give the element an appropriate role value.

(lint/a11y/noStaticElementInteractions)


[error] 35-46: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)

🤖 Prompt for AI Agents
In
health/micro-ui/web/packages/modules/campaign-manager/src/components/AppPreview.js
around lines 35 to 49, the div wrapper for each field is clickable but not
keyboard-accessible; add role="button" and tabIndex={0}, and implement an
onKeyDown handler that listens for Enter (key === "Enter") and Space (key === "
" or key === "Spacebar") to call the same onFieldClick handler (and
preventDefault for Space to avoid page scroll); ensure the handler checks
onFieldClick exists and reuses the same parameters used by onClick so keyboard
users can activate the field.

}
return (
<FieldV1
charCount={field?.charCount}
config={{
step: "",
}}
description={field?.isMdms ? t(field?.helpText) : field?.helpText || null}
error={field?.isMdms ? t(field?.errorMessage) : field?.errorMessage || null}
infoMessage={field?.isMdms ? t(field?.tooltip) : field?.tooltip || null}
label={
getFieldType(field) === "checkbox" || getFieldType(field) === "button" || getFieldType(field) === "custom"
? null
: field?.isMdms
? t(field?.label)
: field?.label
}
onChange={function noRefCheck() {}}
placeholder={t(field?.innerLabel) || ""}
populators={{
t: field?.isMdms ? null : t,
prefix: field?.prefixText,
suffix: field?.suffixText,
title: field?.label,
fieldPairClassName: `app-preview-field-pair ${
selectedField?.jsonPath && selectedField?.jsonPath === field?.jsonPath
? `app-preview-selected`
: selectedField?.id && selectedField?.id === field?.id
? `app-preview-selected`
: ``
} ${field?.["toArray.required"] && getFieldType(field) !== "custom" ? `required` : ``}`,
mdmsConfig: field?.isMdms
? {
moduleName: field?.schemaCode?.split(".")[0],
masterName: field?.schemaCode?.split(".")[1],
}
: null,
options: field?.isMdms ? null : field?.dropDownOptions,
optionsKey: field?.isMdms ? "code" : "name",
component:
getFieldType(field) === "button" || getFieldType(field) === "select" || getFieldType(field) === "custom"
? renderField(field, t)
: null,
}}
type={getFieldType(field) === "button" || getFieldType(field) === "select" ? "custom" : getFieldType(field) || "text"}
value={field?.value === true ? "" : field?.value || ""}
disabled={field?.readOnly || false}
/>
);
})}
{data.type !== "template" && (
<Button
className="app-preview-action-button"
variation="primary"
label={t(data?.actionLabel)}
title={t(data?.actionLabel)}
onClick={() => {}}
/>
)}
{data.type === "template" && (
<GenericTemplateScreen components={card.fields} selectedField={selectedField} t={t} templateName={data.name} />
)}
</Card>
))}
})}
</Fragment>
))}

{/* RENDERING FOOTER */}
{data?.footer?.length > 0 &&
data?.footer?.map((footer_item) => {
return (
<Button
className="app-preview-action-button"
variation="primary"
label={t(footer_item?.label)}
title={t(footer_item?.label)}
onClick={() => {}}
/>
);
})}
</Card>
</div>
</MobileBezelFrame>
);
Expand Down
Loading