diff --git a/src/pages/moderation.jsx b/src/pages/moderation.jsx
index 2ef1175..8c7d773 100644
--- a/src/pages/moderation.jsx
+++ b/src/pages/moderation.jsx
@@ -46,8 +46,10 @@ import {
getFormTemplate,
shouldHideBeneficiaryName,
stripSystemFields,
+ LANGUAGE_MAP,
} from "./moderation/constants";
import { getDynamicMarkerIcon } from "./moderation/helper";
+import { getBlocks } from "./base_function";
const getToken = () => sessionStorage.getItem("accessToken");
@@ -134,6 +136,10 @@ const SelectionPage = ({
const [organizations, setOrganizations] = useState([]);
const [selectedPlan, setSelectedPlan] = useState(initialPlan);
const [selectedForm, setSelectedForm] = useState(initialForm);
+ const [blocksMap, setBlocksMap] = useState({});
+ const [orgPlanCounts, setOrgPlanCounts] = useState({});
+ const [projectPlanCounts, setProjectPlanCounts] = useState({});
+ const [formSubmissionCounts, setFormSubmissionCounts] = useState({});
useEffect(() => {
if (!isSuperAdmin) return;
@@ -142,7 +148,32 @@ const SelectionPage = ({
.then((res) => res.json())
.then((data) => {
const list = data.data || data.results || data;
- setOrganizations(Array.isArray(list) ? list : []);
+ const orgList = Array.isArray(list) ? list : [];
+ setOrganizations(orgList);
+
+ // Fetch plan counts for each org
+ orgList.forEach((org) => {
+ Promise.all([
+ fetch(
+ `${BASEURL}api/v1/plan_count/?org_id=${org.id}`,
+ { headers: getHeaders() }
+ ).then((r) => r.json()),
+ fetch(
+ `${BASEURL}api/v1/plan_count/?org_id=${org.id}&is_completed=true`,
+ { headers: getHeaders() }
+ ).then((r) => r.json())
+ ])
+ .then(([totalData, completedData]) => {
+ const total = totalData.plan_count || 0;
+ const completed = completedData.plan_count || 0;
+
+ setOrgPlanCounts((prev) => ({
+ ...prev,
+ [org.id]: { total, completed },
+ }));
+ })
+ .catch(() => { });
+ });
})
.catch((err) => console.error("Org fetch error", err));
}, [isSuperAdmin]);
@@ -154,7 +185,33 @@ const SelectionPage = ({
.then((res) => res.json())
.then((data) => {
const list = data.data || data.projects || data;
- setProjects(Array.isArray(list) ? list : []);
+ const projList = Array.isArray(list) ? list : [];
+ setProjects(projList);
+
+ // Fetch plan counts for each project
+ projList.forEach((proj) => {
+ const pid = proj.id || proj.project_id;
+ Promise.all([
+ fetch(
+ `${BASEURL}api/v1/plan_count/?project_id=${pid}`,
+ { headers: getHeaders() }
+ ).then((r) => r.json()),
+ fetch(
+ `${BASEURL}api/v1/plan_count/?project_id=${pid}&is_completed=true`,
+ { headers: getHeaders() }
+ ).then((r) => r.json())
+ ])
+ .then(([totalData, completedData]) => {
+ const total = totalData.plan_count || 0;
+ const completed = completedData.plan_count || 0;
+
+ setProjectPlanCounts((prev) => ({
+ ...prev,
+ [pid]: { total, completed },
+ }));
+ })
+ .catch(() => { });
+ });
})
.catch((err) => console.log(err));
}, [isSuperAdmin]);
@@ -172,7 +229,33 @@ const SelectionPage = ({
.then((res) => res.json())
.then((data) => {
const list = data.data || data.projects || data;
- setProjects(Array.isArray(list) ? list : []);
+ const projList = Array.isArray(list) ? list : [];
+ setProjects(projList);
+
+ // Fetch plan counts for each project
+ projList.forEach((proj) => {
+ const pid = proj.id || proj.project_id;
+ Promise.all([
+ fetch(
+ `${BASEURL}api/v1/plan_count/?project_id=${pid}`,
+ { headers: getHeaders() }
+ ).then((r) => r.json()),
+ fetch(
+ `${BASEURL}api/v1/plan_count/?project_id=${pid}&is_completed=true`,
+ { headers: getHeaders() }
+ ).then((r) => r.json())
+ ])
+ .then(([totalData, completedData]) => {
+ const total = totalData.plan_count || 0;
+ const completed = completedData.plan_count || 0;
+
+ setProjectPlanCounts((prev) => ({
+ ...prev,
+ [pid]: { total, completed },
+ }));
+ })
+ .catch(() => { });
+ });
})
.catch((err) => console.log(err));
}, [isSuperAdmin, selectedOrg]);
@@ -216,6 +299,34 @@ const SelectionPage = ({
}
}, [plans, initialPlan]);
+ useEffect(() => {
+ if (!plans || plans.length === 0) return;
+
+ const uniqueDistricts = [
+ ...new Set(plans.map((p) => p.district_soi).filter(Boolean)),
+ ];
+
+ const fetchAllBlocks = async () => {
+ for (const districtCode of uniqueDistricts) {
+ try {
+ const blocks = await getBlocks(districtCode);
+ const blockObj = {};
+ blocks.forEach((block) => {
+ blockObj[block.id] = block.block_name;
+ });
+ setBlocksMap((prev) => ({
+ ...prev,
+ ...blockObj,
+ }));
+ } catch (err) {
+ console.error(`Failed to fetch blocks for district ${districtCode}`, err);
+ }
+ }
+ };
+
+ fetchAllBlocks();
+ }, [plans]);
+
const formatPlansForDropdown = (rawPlans = []) =>
rawPlans.map((p) => ({
plan_id: p.id || p.plan_id,
@@ -224,6 +335,11 @@ const SelectionPage = ({
year: p.created_at ? new Date(p.created_at).getFullYear() : "",
village: p.village || p.village_name || "",
created_at: p.created_at || "",
+ tehsil_soi: p.tehsil_soi,
+ district_soi: p.district_soi,
+ is_completed: p.is_completed ?? false,
+ is_dpr_reviewed: p.is_dpr_reviewed ?? false,
+ is_dpr_approved: p.is_dpr_approved ?? false,
}));
const handleProjectChange = (e) => {
@@ -267,6 +383,29 @@ const SelectionPage = ({
);
};
+ // Fetch submission counts for all forms when a plan is selected
+ useEffect(() => {
+ if (!selectedPlan) {
+ setFormSubmissionCounts({});
+ return;
+ }
+
+ forms.forEach((form) => {
+ fetch(
+ `${BASEURL}api/v1/submissions/${form.name}/${selectedPlan}/`,
+ { headers: getHeaders() },
+ )
+ .then((res) => res.json())
+ .then((data) => {
+ setFormSubmissionCounts((prev) => ({
+ ...prev,
+ [form.name]: data.total_objects ?? data.data?.length ?? 0,
+ }));
+ })
+ .catch(() => { });
+ });
+ }, [selectedPlan, forms]);
+
const handleLoadSubmissions = () => {
if (!selectedForm || !selectedPlan) return;
@@ -277,6 +416,27 @@ const SelectionPage = ({
onLoadSubmissions(selectedProject, selectedPlan, selectedForm, planName);
};
+ const getPlanCategory = (plan) => {
+ if (plan.is_completed) return "Completed";
+ return "In Progress";
+ };
+
+ const PLAN_CATEGORY_ORDER = ["Completed", "In Progress"];
+
+ const groupPlansForDropdown = (plans) => {
+ const groups = {};
+
+ plans.forEach((plan) => {
+ const category = getPlanCategory(plan);
+ if (!groups[category]) groups[category] = [];
+ groups[category].push({ value: plan.plan_id, label: plan.plan, plan });
+ });
+
+ return PLAN_CATEGORY_ORDER
+ .filter((cat) => groups[cat])
+ .map((cat) => ({ label: cat, options: groups[cat] }));
+ };
+
return (
@@ -301,12 +461,13 @@ const SelectionPage = ({
options={organizations.map((org) => ({
value: org.id,
label: org.name,
+ org,
}))}
value={
selectedOrg
? organizations
- .map((org) => ({ value: org.id, label: org.name }))
- .find((o) => o.value === selectedOrg)
+ .map((org) => ({ value: org.id, label: org.name, org }))
+ .find((o) => o.value === selectedOrg)
: null
}
onChange={(opt) => {
@@ -314,6 +475,40 @@ const SelectionPage = ({
setSelectedProject("");
setPlans([]);
}}
+ formatOptionLabel={({ org, label }, { context }) => {
+ const counts = orgPlanCounts[org?.id];
+ if (context === "value") {
+ return (
+
+ {label}
+ {counts && (
+
+ ({counts.total} plans, {counts.completed} completed)
+
+ )}
+
+ );
+ }
+ return (
+
+
+ {label}
+
+ {counts && (
+
+
+
+ {counts.total} Plans
+
+
+
+ {counts.completed} Completed
+
+
+ )}
+
+ );
+ }}
isClearable
/>
@@ -329,20 +524,57 @@ const SelectionPage = ({
options={projects.map((p) => ({
value: p.id || p.project_id,
label: p.project_name || p.name,
+ project: p,
}))}
value={
selectedProject
? projects
- .map((p) => ({
- value: p.id || p.project_id,
- label: p.project_name || p.name,
- }))
- .find((p) => p.value === selectedProject)
+ .map((p) => ({
+ value: p.id || p.project_id,
+ label: p.project_name || p.name,
+ project: p,
+ }))
+ .find((p) => p.value === selectedProject)
: null
}
onChange={(opt) =>
handleProjectChange({ target: { value: opt?.value || "" } })
}
+ formatOptionLabel={({ project, label }, { context }) => {
+ const pid = project?.id || project?.project_id;
+ const counts = projectPlanCounts[pid];
+ if (context === "value") {
+ return (
+
+ {label}
+ {counts && (
+
+ ({counts.total} plans, {counts.completed} completed)
+
+ )}
+
+ );
+ }
+ return (
+
+
+ {label}
+
+ {counts && (
+
+
+
+ {counts.total} Plans
+
+
+
+ {counts.completed} Completed
+
+
+ )}
+
+ );
+ }}
isClearable
/>
@@ -366,20 +598,16 @@ const SelectionPage = ({
}),
}}
placeholder="-- Choose Plan --"
- options={plans.map((plan) => ({
- value: plan.plan_id,
- label: plan.plan,
- plan,
- }))}
+ options={groupPlansForDropdown(plans)}
value={
selectedPlan
? plans
- .map((plan) => ({
- value: plan.plan_id,
- label: plan.plan,
- plan,
- }))
- .find((p) => p.value === Number(selectedPlan))
+ .map((plan) => ({
+ value: plan.plan_id,
+ label: plan.plan,
+ plan,
+ }))
+ .find((p) => p.value === Number(selectedPlan))
: null
}
onChange={(opt) => setSelectedPlan(opt?.value || "")}
@@ -393,10 +621,10 @@ const SelectionPage = ({
}
const date = plan.created_at
? new Date(plan.created_at).toLocaleDateString("en-IN", {
- day: "2-digit",
- month: "short",
- year: "numeric",
- })
+ day: "2-digit",
+ month: "short",
+ year: "numeric",
+ })
: null;
return (
@@ -463,6 +691,24 @@ const SelectionPage = ({
{date}
)}
+ {plan.tehsil_soi && (
+
+
+ {blocksMap[plan.tehsil_soi] || `Tehsil (${plan.tehsil_soi})`}
+
+ )}
);
@@ -482,14 +728,44 @@ const SelectionPage = ({
value={
selectedForm
? forms
- .map((form) => ({
- value: form.name,
- label: FORM_DISPLAY_NAMES[form.name] || form.name,
- }))
- .find((f) => f.value === selectedForm)
+ .map((form) => ({
+ value: form.name,
+ label: FORM_DISPLAY_NAMES[form.name] || form.name,
+ }))
+ .find((f) => f.value === selectedForm)
: null
}
onChange={(opt) => setSelectedForm(opt?.value || "")}
+ formatOptionLabel={({ value, label }, { context }) => {
+ const count = formSubmissionCounts[value];
+ if (context === "value") {
+ return (
+
+ {label}
+ {count !== undefined && (
+
+ ({count} submissions)
+
+ )}
+
+ );
+ }
+ return (
+
+
+ {label}
+
+ {count !== undefined && (
+
+
+
+ {count} Submissions
+
+
+ )}
+
+ );
+ }}
isClearable
/>
@@ -529,6 +805,7 @@ const FormViewPage = ({
const [isEditing, setIsEditing] = useState(false);
const [dprExpanded, setDprExpanded] = useState(false);
const [dprEmail, setDprEmail] = useState("");
+ const [dprLanguage, setDprLanguage] = useState("en");
const [dprLoading, setDprLoading] = useState(false);
const [dprNotification, setDprNotification] = useState(null);
const [planDetails, setPlanDetails] = useState(null);
@@ -551,7 +828,7 @@ const FormViewPage = ({
const [validationLoading, setValidationLoading] = useState({});
const [saveStatus, setSaveStatus] = useState("idle");
const [saveError, setSaveError] = useState("");
- const [deleteStatus, setDeleteStatus] = useState("idle");
+ const [deleteStatus, setDeleteStatus] = useState("idle");
const [deleteError, setDeleteError] = useState("");
const [submissionToDelete, setSubmissionToDelete] = useState(null);
const [formTemplateLoading, setFormTemplateLoading] = useState(false);
@@ -616,8 +893,8 @@ const FormViewPage = ({
if (!res.ok) {
throw new Error(
data?.message ||
- data?.error ||
- "Failed to fetch DPR workflow status.",
+ data?.error ||
+ "Failed to fetch DPR workflow status.",
);
}
return data;
@@ -636,7 +913,7 @@ const FormViewPage = ({
});
}, [selectedPlan]);
- const reloadSubmissions = (resetToPage1 = false) => {
+ const reloadSubmissions = (resetToPage1 = false) => {
if (viewMode === "map") {
fetchSubmissions(1, "map");
} else {
@@ -798,216 +1075,216 @@ const FormViewPage = ({
};
// Generic function to analyze form schema and identify field types
-const analyzeFormSchema = (schema) => {
- const fieldTypes = {};
-
- const analyzeElement = (element, parentName = "") => {
- const elementName =
- element.name.startsWith(parentName + "-")
- ? element.name
- : parentName
- ? `${parentName}-${element.name}`
- : element.name;
-
- if (element.type === "checkbox") {
- fieldTypes[elementName] = "checkbox";
- fieldTypes[element.name] = "checkbox"; // also register bare name
- } else if (element.type === "radiogroup") {
- fieldTypes[elementName] = "radio";
- fieldTypes[element.name] = "radio";
- } else if (element.type === "multipletext") {
- fieldTypes[elementName] = "multipletext";
- fieldTypes[element.name] = "multipletext";
- } else if (element.type === "panel") {
- if (element.elements) {
- element.elements.forEach((child) => {
- if (child.name.includes("-")) {
- analyzeElement(child, "");
- } else {
- analyzeElement(child, element.name);
- }
- });
+ const analyzeFormSchema = (schema) => {
+ const fieldTypes = {};
+
+ const analyzeElement = (element, parentName = "") => {
+ const elementName =
+ element.name.startsWith(parentName + "-")
+ ? element.name
+ : parentName
+ ? `${parentName}-${element.name}`
+ : element.name;
+
+ if (element.type === "checkbox") {
+ fieldTypes[elementName] = "checkbox";
+ fieldTypes[element.name] = "checkbox"; // also register bare name
+ } else if (element.type === "radiogroup") {
+ fieldTypes[elementName] = "radio";
+ fieldTypes[element.name] = "radio";
+ } else if (element.type === "multipletext") {
+ fieldTypes[elementName] = "multipletext";
+ fieldTypes[element.name] = "multipletext";
+ } else if (element.type === "panel") {
+ if (element.elements) {
+ element.elements.forEach((child) => {
+ if (child.name.includes("-")) {
+ analyzeElement(child, "");
+ } else {
+ analyzeElement(child, element.name);
+ }
+ });
+ }
}
- }
- if (element.items && Array.isArray(element.items)) {
- fieldTypes[elementName] = "multipletext";
- fieldTypes[element.name] = "multipletext";
- }
- };
-
- if (schema.pages) {
- schema.pages.forEach((page) => {
- if (page.elements) {
- page.elements.forEach((element) => analyzeElement(element));
+ if (element.items && Array.isArray(element.items)) {
+ fieldTypes[elementName] = "multipletext";
+ fieldTypes[element.name] = "multipletext";
}
- });
- }
-
- return fieldTypes;
-};
+ };
- const buildChoiceMap = (schema) => {
- const choiceMap = {};
- const processElement = (element) => {
- if (
- (element.type === "radiogroup" ||
- element.type === "checkbox" ||
- element.type === "dropdown") &&
- Array.isArray(element.choices)
- ) {
- const map = {};
- element.choices.forEach((choice) => {
- if (typeof choice === "object" && choice.value !== undefined) {
- map[String(choice.value).toLowerCase().trim()] = choice.value;
- const text =
- typeof choice.text === "object"
- ? choice.text?.default ?? Object.values(choice.text)[0]
- : choice.text;
- if (text) map[String(text).toLowerCase().trim()] = choice.value;
- } else if (typeof choice === "string") {
- map[choice.toLowerCase().trim()] = choice;
+ if (schema.pages) {
+ schema.pages.forEach((page) => {
+ if (page.elements) {
+ page.elements.forEach((element) => analyzeElement(element));
}
});
- choiceMap[element.name] = map;
}
- if (element.elements) element.elements.forEach(processElement);
+
+ return fieldTypes;
};
- if (schema.pages)
- schema.pages.forEach((page) =>
- (page.elements || []).forEach(processElement)
- );
- return choiceMap;
-};
-const resolveCheckboxValues = (rawString, fieldChoices) => {
- if (!rawString || typeof rawString !== "string") return [];
- const trimmed = rawString.trim();
- if (!trimmed) return [];
- if (!fieldChoices)
- return trimmed.split(" ").filter((v) => v.trim().length > 0);
-
- // Strategy 1: space-split — if ALL tokens match known values
- const spaceSplit = trimmed.split(" ").filter((v) => v.trim().length > 0);
- const allMatch = spaceSplit.every(
- (t) => fieldChoices[t.toLowerCase().trim()] !== undefined
- );
- if (allMatch)
- return spaceSplit.map((t) => fieldChoices[t.toLowerCase().trim()]);
-
- // Strategy 2: capital-letter split for multi-word values
- const capitalSplit = trimmed
- .split(/(?=[A-Z])/)
- .map((t) => t.trim())
- .filter((t) => t.length > 0);
- return capitalSplit.map((t) => fieldChoices[t.toLowerCase().trim()] ?? t);
-};
+ const buildChoiceMap = (schema) => {
+ const choiceMap = {};
+ const processElement = (element) => {
+ if (
+ (element.type === "radiogroup" ||
+ element.type === "checkbox" ||
+ element.type === "dropdown") &&
+ Array.isArray(element.choices)
+ ) {
+ const map = {};
+ element.choices.forEach((choice) => {
+ if (typeof choice === "object" && choice.value !== undefined) {
+ map[String(choice.value).toLowerCase().trim()] = choice.value;
+ const text =
+ typeof choice.text === "object"
+ ? choice.text?.default ?? Object.values(choice.text)[0]
+ : choice.text;
+ if (text) map[String(text).toLowerCase().trim()] = choice.value;
+ } else if (typeof choice === "string") {
+ map[choice.toLowerCase().trim()] = choice;
+ }
+ });
+ choiceMap[element.name] = map;
+ }
+ if (element.elements) element.elements.forEach(processElement);
+ };
+ if (schema.pages)
+ schema.pages.forEach((page) =>
+ (page.elements || []).forEach(processElement)
+ );
+ return choiceMap;
+ };
-const resolveRadioValue = (rawValue, fieldChoices) => {
- if (!rawValue || typeof rawValue !== "string") return rawValue;
- if (!fieldChoices) return rawValue;
- const resolved = fieldChoices[rawValue.toLowerCase().trim()];
- return resolved !== undefined ? resolved : rawValue;
-};
+ const resolveCheckboxValues = (rawString, fieldChoices) => {
+ if (!rawString || typeof rawString !== "string") return [];
+ const trimmed = rawString.trim();
+ if (!trimmed) return [];
+ if (!fieldChoices)
+ return trimmed.split(" ").filter((v) => v.trim().length > 0);
+
+ // Strategy 1: space-split — if ALL tokens match known values
+ const spaceSplit = trimmed.split(" ").filter((v) => v.trim().length > 0);
+ const allMatch = spaceSplit.every(
+ (t) => fieldChoices[t.toLowerCase().trim()] !== undefined
+ );
+ if (allMatch)
+ return spaceSplit.map((t) => fieldChoices[t.toLowerCase().trim()]);
+
+ // Strategy 2: capital-letter split for multi-word values
+ const capitalSplit = trimmed
+ .split(/(?=[A-Z])/)
+ .map((t) => t.trim())
+ .filter((t) => t.length > 0);
+ return capitalSplit.map((t) => fieldChoices[t.toLowerCase().trim()] ?? t);
+ };
+
+ const resolveRadioValue = (rawValue, fieldChoices) => {
+ if (!rawValue || typeof rawValue !== "string") return rawValue;
+ if (!fieldChoices) return rawValue;
+ const resolved = fieldChoices[rawValue.toLowerCase().trim()];
+ return resolved !== undefined ? resolved : rawValue;
+ };
// Transform API data to SurveyJS format
-const transformApiToSurvey = (submission, formSchema) => {
- const fieldTypes = analyzeFormSchema(formSchema);
- const choiceMap = buildChoiceMap(formSchema);
- const transformedData = { ...submission };
-
- const processObject = (obj, parentKey = "") => {
- Object.keys(obj).forEach((key) => {
- const value = obj[key];
- const fullKey = parentKey ? `${parentKey}-${key}` : key;
-
- if (value && typeof value === "object" && !Array.isArray(value)) {
- // GPS_point special handling
- if (key === "GPS_point") {
- const coordsObj =
- value.point_mapsappearance || value.point_mapappearance;
- if (coordsObj?.coordinates) {
- const coords = coordsObj.coordinates;
- transformedData["GPS_point"] = {
- longitude: coords[0],
- latitude: coords[1],
- };
- return;
+ const transformApiToSurvey = (submission, formSchema) => {
+ const fieldTypes = analyzeFormSchema(formSchema);
+ const choiceMap = buildChoiceMap(formSchema);
+ const transformedData = { ...submission };
+
+ const processObject = (obj, parentKey = "") => {
+ Object.keys(obj).forEach((key) => {
+ const value = obj[key];
+ const fullKey = parentKey ? `${parentKey}-${key}` : key;
+
+ if (value && typeof value === "object" && !Array.isArray(value)) {
+ // GPS_point special handling
+ if (key === "GPS_point") {
+ const coordsObj =
+ value.point_mapsappearance || value.point_mapappearance;
+ if (coordsObj?.coordinates) {
+ const coords = coordsObj.coordinates;
+ transformedData["GPS_point"] = {
+ longitude: coords[0],
+ latitude: coords[1],
+ };
+ return;
+ }
+ if (value.latitude !== undefined && value.longitude !== undefined) {
+ transformedData["GPS_point"] = value;
+ return;
+ }
}
+
if (value.latitude !== undefined && value.longitude !== undefined) {
- transformedData["GPS_point"] = value;
+ transformedData[fullKey] = value;
return;
}
- }
- if (value.latitude !== undefined && value.longitude !== undefined) {
- transformedData[fullKey] = value;
- return;
- }
+ // multipletext → keep as nested object, never flatten
+ const isMultipleText =
+ fieldTypes[key] === "multipletext" ||
+ fieldTypes[fullKey] === "multipletext";
- // multipletext → keep as nested object, never flatten
- const isMultipleText =
- fieldTypes[key] === "multipletext" ||
- fieldTypes[fullKey] === "multipletext";
-
- if (isMultipleText) {
- transformedData[key] = value;
- return;
- }
+ if (isMultipleText) {
+ transformedData[key] = value;
+ return;
+ }
- // Regular nested object (panel) → flatten with "-" separator
- processObject(value, key);
- } else {
- if (typeof value === "string" && value.trim().length > 0) {
- const fieldType = fieldTypes[fullKey] || fieldTypes[key];
- const fieldChoices = choiceMap[fullKey] || choiceMap[key];
-
- if (fieldType === "checkbox") {
- transformedData[fullKey] = resolveCheckboxValues(value, fieldChoices);
- } else if (fieldType === "radio") {
- transformedData[fullKey] = resolveRadioValue(value, fieldChoices);
+ // Regular nested object (panel) → flatten with "-" separator
+ processObject(value, key);
+ } else {
+ if (typeof value === "string" && value.trim().length > 0) {
+ const fieldType = fieldTypes[fullKey] || fieldTypes[key];
+ const fieldChoices = choiceMap[fullKey] || choiceMap[key];
+
+ if (fieldType === "checkbox") {
+ transformedData[fullKey] = resolveCheckboxValues(value, fieldChoices);
+ } else if (fieldType === "radio") {
+ transformedData[fullKey] = resolveRadioValue(value, fieldChoices);
+ } else {
+ // text/number input — use as-is
+ transformedData[fullKey] = value;
+ }
} else {
- // text/number input — use as-is
+ // null, undefined, number, boolean — pass through directly
transformedData[fullKey] = value;
}
- } else {
- // null, undefined, number, boolean — pass through directly
- transformedData[fullKey] = value;
}
+ });
+ };
+
+ processObject(submission);
+
+ // Legacy key mappings
+ const legacyKeyMap = {
+ "Livestock-is_demand_livestock": "select_one_demand_promoting_livestock",
+ "Livestock-demands_promoting_livestock": "select_one_promoting_livestock",
+ "Livestock-select_one_promoting_livestock_other":
+ "select_one_promoting_livestock_other",
+ "kitchen_gardens-area_kg": "area_didi_badi",
+ "kitchen_gardens-assets_kg": "indi_assets",
+ "fisheries-is_demand_fisheries": "select_one_demand_promoting_fisheries",
+ "fisheries-demands_promoting_fisheries": "select_one_promoting_fisheries",
+ "fisheries-demands_promoting_fisheries_other":
+ "select_one_promoting_fisheries_other",
+ };
+
+ Object.entries(legacyKeyMap).forEach(([newKey, oldKey]) => {
+ const oldValue = submission[oldKey];
+ const currentValue = transformedData[newKey];
+ const isCurrentEmpty =
+ currentValue === null || currentValue === undefined || currentValue === "";
+ const isOldValueReal =
+ oldValue !== null && oldValue !== undefined && oldValue !== "";
+ if (isCurrentEmpty && isOldValueReal) {
+ transformedData[newKey] = oldValue;
}
});
- };
- processObject(submission);
-
- // Legacy key mappings
- const legacyKeyMap = {
- "Livestock-is_demand_livestock": "select_one_demand_promoting_livestock",
- "Livestock-demands_promoting_livestock": "select_one_promoting_livestock",
- "Livestock-select_one_promoting_livestock_other":
- "select_one_promoting_livestock_other",
- "kitchen_gardens-area_kg": "area_didi_badi",
- "kitchen_gardens-assets_kg": "indi_assets",
- "fisheries-is_demand_fisheries": "select_one_demand_promoting_fisheries",
- "fisheries-demands_promoting_fisheries": "select_one_promoting_fisheries",
- "fisheries-demands_promoting_fisheries_other":
- "select_one_promoting_fisheries_other",
+ return transformedData;
};
- Object.entries(legacyKeyMap).forEach(([newKey, oldKey]) => {
- const oldValue = submission[oldKey];
- const currentValue = transformedData[newKey];
- const isCurrentEmpty =
- currentValue === null || currentValue === undefined || currentValue === "";
- const isOldValueReal =
- oldValue !== null && oldValue !== undefined && oldValue !== "";
- if (isCurrentEmpty && isOldValueReal) {
- transformedData[newKey] = oldValue;
- }
- });
-
- return transformedData;
-};
-
// Transform SurveyJS data back to API format
const transformSurveyToApi = (surveyData, originalSubmission, formSchema) => {
const fieldTypes = analyzeFormSchema(formSchema);
@@ -1431,100 +1708,100 @@ const transformApiToSurvey = (submission, formSchema) => {
};
}, [submissions]);
-const handleViewSubmission = async (submission) => {
- setFormTemplateLoading(true);
- const formTemplate = await getFormTemplate(selectedForm);
- setFormTemplateLoading(false);
- if (!formTemplate) {
- alert(`No template found for form: ${selectedForm}`);
- return;
- }
-
- const cleanTemplate = stripSystemFields(formTemplate);
- const transformedData = transformApiToSurvey(submission, formTemplate);
-
- setSelectedSubmission(submission);
- setIsEditing(false);
-
- const model = new Model(cleanTemplate);
- model.mode = "display";
- model.data = transformedData;
- setSurveyModel(model);
-};
+ const handleViewSubmission = async (submission) => {
+ setFormTemplateLoading(true);
+ const formTemplate = await getFormTemplate(selectedForm);
+ setFormTemplateLoading(false);
+ if (!formTemplate) {
+ alert(`No template found for form: ${selectedForm}`);
+ return;
+ }
+ const cleanTemplate = stripSystemFields(formTemplate);
+ const transformedData = transformApiToSurvey(submission, formTemplate);
-const handleEditSubmission = async (submission) => {
- setFormTemplateLoading(true);
- const formTemplate = await getFormTemplate(selectedForm);
- setFormTemplateLoading(false);
- if (!formTemplate) {
- alert(`No template found for form: ${selectedForm}`);
- return;
- }
+ setSelectedSubmission(submission);
+ setIsEditing(false);
- const cleanTemplate = stripSystemFields(formTemplate);
- const transformedData = transformApiToSurvey(submission, formTemplate);
+ const model = new Model(cleanTemplate);
+ model.mode = "display";
+ model.data = transformedData;
+ setSurveyModel(model);
+ };
- setSelectedSubmission(submission);
- setIsEditing(true);
- const model = new Model(cleanTemplate);
- model.showCompletedPage = false;
- model.data = transformedData;
+ const handleEditSubmission = async (submission) => {
+ setFormTemplateLoading(true);
+ const formTemplate = await getFormTemplate(selectedForm);
+ setFormTemplateLoading(false);
+ if (!formTemplate) {
+ alert(`No template found for form: ${selectedForm}`);
+ return;
+ }
- model.onComplete.add((sender) => {
- const saveData = transformSurveyToApi(
- sender.data,
- submission,
- formTemplate,
- );
- const uuid = getSubmissionUUID(submission);
- handleSaveSubmission(uuid, saveData);
- });
+ const cleanTemplate = stripSystemFields(formTemplate);
+ const transformedData = transformApiToSurvey(submission, formTemplate);
- setSurveyModel(model);
-};
+ setSelectedSubmission(submission);
+ setIsEditing(true);
-const handleSaveSubmission = async (uuid, data) => {
- setSaveStatus("saving");
- setSaveError("");
- try {
- const response = await fetch(
- `${BASEURL}api/v1/submissions/${selectedForm}/${uuid}/modify/`,
- {
- method: "PUT",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${sessionStorage.getItem("accessToken")}`,
+ const model = new Model(cleanTemplate);
+ model.showCompletedPage = false;
+ model.data = transformedData;
+
+ model.onComplete.add((sender) => {
+ const saveData = transformSurveyToApi(
+ sender.data,
+ submission,
+ formTemplate,
+ );
+ const uuid = getSubmissionUUID(submission);
+ handleSaveSubmission(uuid, saveData);
+ });
+
+ setSurveyModel(model);
+ };
+
+ const handleSaveSubmission = async (uuid, data) => {
+ setSaveStatus("saving");
+ setSaveError("");
+ try {
+ const response = await fetch(
+ `${BASEURL}api/v1/submissions/${selectedForm}/${uuid}/modify/`,
+ {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${sessionStorage.getItem("accessToken")}`,
+ },
+ body: JSON.stringify(data),
},
- body: JSON.stringify(data),
- },
- );
+ );
- const result = await response.json();
+ const result = await response.json();
- if (!response.ok) {
+ if (!response.ok) {
+ setSaveStatus("error");
+ setSaveError(result?.message || result?.error || "Save failed. Please try again.");
+ return;
+ }
+
+ setSaveStatus("success");
+ reloadSubmissions(true);
+
+ // Auto close after 1.5s on success
+ setTimeout(() => {
+ setSelectedSubmission(null);
+ setSurveyModel(null);
+ setSaveStatus("idle");
+ }, 1500);
+
+ } catch (error) {
+ console.error("Save error:", error);
setSaveStatus("error");
- setSaveError(result?.message || result?.error || "Save failed. Please try again.");
- return;
+ setSaveError("Network error. Please try again.");
}
-
- setSaveStatus("success");
- reloadSubmissions(true);
-
- // Auto close after 1.5s on success
- setTimeout(() => {
- setSelectedSubmission(null);
- setSurveyModel(null);
- setSaveStatus("idle");
- }, 1500);
-
- } catch (error) {
- console.error("Save error:", error);
- setSaveStatus("error");
- setSaveError("Network error. Please try again.");
- }
-};
+ };
const handleValidateSubmission = async (submission) => {
const uuid = getSubmissionUUID(submission);
@@ -1584,7 +1861,7 @@ const handleSaveSubmission = async (uuid, data) => {
setDeleteStatus("error");
setDeleteError("Network error. Please try again.");
}
-};
+ };
const handleGenerateDPR = async () => {
if (!dprEmail || !selectedPlan) return;
@@ -1600,6 +1877,7 @@ const handleSaveSubmission = async (uuid, data) => {
body: JSON.stringify({
plan_id: Number(selectedPlan),
email_id: dprEmail,
+ language: dprLanguage,
}),
});
const data = await response.json();
@@ -1609,6 +1887,7 @@ const handleSaveSubmission = async (uuid, data) => {
message: data.message || "DPR generation request sent successfully!",
});
setDprEmail("");
+ setDprLanguage("en");
setDprExpanded(false);
} else {
setDprNotification({
@@ -1649,8 +1928,8 @@ const handleSaveSubmission = async (uuid, data) => {
if (!response.ok) {
throw new Error(
data?.message ||
- data?.error ||
- "Failed to update DPR workflow status.",
+ data?.error ||
+ "Failed to update DPR workflow status.",
);
}
@@ -1826,22 +2105,20 @@ const handleSaveSubmission = async (uuid, data) => {
@@ -2037,24 +2324,21 @@ const handleSaveSubmission = async (uuid, data) => {
)
}
disabled={planReviewLoading}
- className={`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-all focus:outline-none focus:ring-2 focus:ring-slate-300 focus:ring-offset-2 ${
- planDetails?.is_reviewed
- ? "bg-blue-700"
- : "bg-slate-300"
- } ${
- planReviewLoading
+ className={`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-all focus:outline-none focus:ring-2 focus:ring-slate-300 focus:ring-offset-2 ${planDetails?.is_reviewed
+ ? "bg-blue-700"
+ : "bg-slate-300"
+ } ${planReviewLoading
? "cursor-not-allowed opacity-60"
: "cursor-pointer"
- }`}
+ }`}
aria-pressed={Boolean(planDetails?.is_reviewed)}
aria-label="Is DPR Reviewed?"
>
@@ -2063,11 +2347,10 @@ const handleSaveSubmission = async (uuid, data) => {
{planReviewNotification && (
{planReviewNotification.message}
@@ -2104,27 +2387,24 @@ const handleSaveSubmission = async (uuid, data) => {
dprWorkflowMissing ||
dprWorkflowLoading === "status-submitted"
}
- className={`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-all focus:outline-none focus:ring-2 focus:ring-slate-300 focus:ring-offset-2 ${
- dprWorkflowStatus?.status === "SUBMITTED"
- ? "bg-blue-700"
- : "bg-slate-300"
- } ${
- dprWorkflowMissing ||
- dprWorkflowLoading === "status-submitted"
+ className={`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-all focus:outline-none focus:ring-2 focus:ring-slate-300 focus:ring-offset-2 ${dprWorkflowStatus?.status === "SUBMITTED"
+ ? "bg-blue-700"
+ : "bg-slate-300"
+ } ${dprWorkflowMissing ||
+ dprWorkflowLoading === "status-submitted"
? "cursor-not-allowed opacity-60"
: "cursor-pointer"
- }`}
+ }`}
aria-pressed={
dprWorkflowStatus?.status === "SUBMITTED"
}
aria-label="DPR submitted"
>
@@ -2169,27 +2449,24 @@ const handleSaveSubmission = async (uuid, data) => {
dprWorkflowMissing ||
dprWorkflowLoading === "status-approved"
}
- className={`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-all focus:outline-none focus:ring-2 focus:ring-slate-300 focus:ring-offset-2 ${
- dprWorkflowStatus?.status === "APPROVED"
- ? "bg-emerald-700"
- : "bg-slate-300"
- } ${
- dprWorkflowMissing ||
- dprWorkflowLoading === "status-approved"
+ className={`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-all focus:outline-none focus:ring-2 focus:ring-slate-300 focus:ring-offset-2 ${dprWorkflowStatus?.status === "APPROVED"
+ ? "bg-emerald-700"
+ : "bg-slate-300"
+ } ${dprWorkflowMissing ||
+ dprWorkflowLoading === "status-approved"
? "cursor-not-allowed opacity-60"
: "cursor-pointer"
- }`}
+ }`}
aria-pressed={
dprWorkflowStatus?.status === "APPROVED"
}
aria-label="DPR approved"
>
@@ -2216,27 +2493,24 @@ const handleSaveSubmission = async (uuid, data) => {
dprWorkflowMissing ||
dprWorkflowLoading === "status-rejected"
}
- className={`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-all focus:outline-none focus:ring-2 focus:ring-slate-300 focus:ring-offset-2 ${
- dprWorkflowStatus?.status === "REJECTED"
- ? "bg-red-700"
- : "bg-slate-300"
- } ${
- dprWorkflowMissing ||
- dprWorkflowLoading === "status-rejected"
+ className={`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-all focus:outline-none focus:ring-2 focus:ring-slate-300 focus:ring-offset-2 ${dprWorkflowStatus?.status === "REJECTED"
+ ? "bg-red-700"
+ : "bg-slate-300"
+ } ${dprWorkflowMissing ||
+ dprWorkflowLoading === "status-rejected"
? "cursor-not-allowed opacity-60"
: "cursor-pointer"
- }`}
+ }`}
aria-pressed={
dprWorkflowStatus?.status === "REJECTED"
}
aria-label="DPR rejected"
>
@@ -2254,11 +2528,10 @@ const handleSaveSubmission = async (uuid, data) => {
{dprWorkflowNotification && (
{dprWorkflowNotification.message}
@@ -2273,18 +2546,16 @@ const handleSaveSubmission = async (uuid, data) => {
{/* Floating toast notification */}
{dprNotification && (
{dprNotification.type === "success" ? (