From 7c8590c07509fc68c3040832bcb5e3fa69302461 Mon Sep 17 00:00:00 2001 From: MehrdadAdabi <126083584+mehrdadAdabi@users.noreply.github.com> Date: Thu, 27 Nov 2025 20:03:05 +0330 Subject: [PATCH] feat: add file uploader component and refactor form field validation - Add FileUploader component import and integration in form-field - Refactor validateField function to accept typed parameters (string | number | boolean) - Move validateField definition before useEffect for better code organization - Improve code formatting and consistency in select case block - Add file upload type handling in renderInput switch statement - Extend FormFieldType interface with comprehensive field properties - Format template strings for better readability This change enhances the form field component to support file uploads and improves type safety in validation logic. The refactoring also improves code maintainability by reorganizing function definitions and standardizing formatting. --- src/core/components/base/form-field.tsx | 99 ++--- src/core/utils/index.ts | 2 +- .../pages/step-form/dynamic-form.tsx | 98 +++-- .../dashboard/pages/step-form/index.tsx | 407 ++++++++++-------- .../pages/step-form/step-form.type.ts | 25 ++ .../dashboard/service/dynamic-form.service.ts | 18 +- 6 files changed, 381 insertions(+), 268 deletions(-) diff --git a/src/core/components/base/form-field.tsx b/src/core/components/base/form-field.tsx index 6da25bd..f021765 100644 --- a/src/core/components/base/form-field.tsx +++ b/src/core/components/base/form-field.tsx @@ -9,6 +9,7 @@ import { import { useEffect, useState, type ChangeEvent, type FC } from "react"; import { BaseDropdown } from "./base-drop-down"; import { CustomCheckbox } from "./checkbox"; +import { FileUploader } from "./file-uploader"; import { ImageUploader } from "./image-uploader"; import { CustomInput } from "./input"; import { PersonSearchInput } from "./person-search-input"; // Import PersonSearchInput @@ -30,11 +31,9 @@ const FormField: FC = ({ }) => { const [error, setError] = useState(null); - useEffect(() => { - validateField(value); - }, [value]); - const validateField = (currentValue: any) => { + + const validateField = (currentValue: string | number | boolean) => { let errorMessage: string | null = null; // Required validation @@ -127,6 +126,10 @@ const FormField: FC = ({ setError(errorMessage); }; + useEffect(() => { + validateField(value); + }, [value]); + const handleChange = ( e: ChangeEvent ) => { @@ -152,9 +155,8 @@ const FormField: FC = ({ value: value || "", onChange: handleChange, required: field.Required, - className: `w-full p-2 border rounded-md text-right ${ - error ? "border-red-500" : "border-gray-300" - }`, + className: `w-full p-2 border rounded-md text-right ${error ? "border-red-500" : "border-gray-300" + }`, dir: "rtl", }; switch (inputType) { @@ -251,14 +253,15 @@ const FormField: FC = ({ /> ); case "select": - const isDependent = field.Parent_FieldID !== undefined; - const parentFieldValue = - isDependent && field.Parent_FieldID !== undefined - ? allFormValues[field.Parent_FieldID.toString()] - : undefined; + { + const isDependent = field.Parent_FieldID !== undefined; + const parentFieldValue = + isDependent && field.Parent_FieldID !== undefined + ? allFormValues[field.Parent_FieldID.toString()] + : undefined; - const fetchDependentOptions = isDependent - ? async (inputValue: string) => { + const fetchDependentOptions = isDependent + ? async (inputValue: string) => { if (!parentFieldValue || field.Parent_FieldID === undefined) return []; // Don't fetch if parent value is not set or Parent_FieldID is undefined const fetched = await FormService.fetchDependentOptions( @@ -269,25 +272,25 @@ const FormField: FC = ({ option.label.toLowerCase().includes(inputValue.toLowerCase()) ); } - : undefined; + : undefined; - return ( - opt.value === value)?.value || - undefined - } - onChange={(optionValue: string) => onChange(optionValue)} - className={`w-full p-2 border rounded-md text-right ${ - error ? "border-red-500" : "border-gray-300" - }`} - fetchOptions={fetchDependentOptions} - disabled={isDependent && !parentFieldValue} // Disable if dependent and parent value is not set - /> - ); + return ( + opt.value === value)?.value || + undefined + } + onChange={(optionValue: string) => onChange(optionValue)} + className={`w-full p-2 border rounded-md text-right ${error ? "border-red-500" : "border-gray-300" + }`} + fetchOptions={fetchDependentOptions} + disabled={isDependent && !parentFieldValue} // Disable if dependent and parent value is not set + /> + ); + } case "radio": return (
@@ -325,19 +328,17 @@ const FormField: FC = ({ onChange(file)} previewImage={value} - className={`w-full p-2 border rounded-md text-right ${ - error ? "border-red-500" : "border-gray-300" - }`} + className={`w-full p-2 border rounded-md text-right ${error ? "border-red-500" : "border-gray-300" + }`} /> ); case "file-upload": return ( - ) => - onChange(e.target.files ? e.target.files[0] : null) - } + + onFileChange={(file) => onChange(file)} + value={""} // File inputs are uncontrolled, value should be empty /> ); @@ -356,15 +357,15 @@ const FormField: FC = ({ {field.Description || field.Name}

); - default: - return ( - - ); + // default: + // return ( + // + // ); } }; diff --git a/src/core/utils/index.ts b/src/core/utils/index.ts index 06e7f1c..2475f68 100644 --- a/src/core/utils/index.ts +++ b/src/core/utils/index.ts @@ -4,7 +4,7 @@ import to from "await-to-js"; import { toast } from "react-toastify"; import { API_ADDRESS } from "../service/api-address"; -export const getContactImageUrl = (stageID: Number): string => { +export const getContactImageUrl = (stageID: number): string => { const token = userInfoService.getToken(); if (!token) { throw new Error("توکن یافت نشد"); diff --git a/src/modules/dashboard/pages/step-form/dynamic-form.tsx b/src/modules/dashboard/pages/step-form/dynamic-form.tsx index 2ca0fa8..d101d34 100644 --- a/src/modules/dashboard/pages/step-form/dynamic-form.tsx +++ b/src/modules/dashboard/pages/step-form/dynamic-form.tsx @@ -8,6 +8,7 @@ import React, { import { CustomButton } from "@/core/components/base/button"; import FormField from "@/core/components/base/form-field"; +import { uploadImage } from "@/core/utils"; import { FieldTypes, FormService, @@ -16,20 +17,37 @@ import { } from "@/core/utils/dynamic-field.utils"; import type { DynamicFormProps } from "./step-form.type"; -const DynamicForm: FC = ({ fields, processId, stepId }) => { - const [formValues, setFormValues] = useState>({}); +const DynamicForm: FC = ({ fields, processId, stepId, workflowId, onChange }) => { + const [formValues, setFormValues] = useState({}); const [_, setFormErrors] = useState>({}); const [isSubmitting, setIsSubmitting] = useState(false); + const evaluateShowCondition = useCallback( + (condition: string, currentFormValues: Record): boolean => { + try { + const parts = condition.split("===").map((s) => s.trim()); + if (parts.length === 2) { + const fieldName = parts[0]; + const expectedValue = parts[1].replace(/^['"]|['"]$/g, ""); // Remove quotes + return currentFormValues[fieldName] == expectedValue; + } + } catch (e) { + console.error("Error evaluating ShowCondition:", e); + } + return true; // Default to visible if condition cannot be parsed + }, + [] + ); + useEffect(() => { - const initialValues: Record = {}; + const initialValues: Record = {}; fields.forEach((field) => { if (field.DefaultValue !== undefined) { - initialValues[field.Name] = field.DefaultValue; + initialValues[field.NameOrID] = field.DefaultValue; } else if (field.Type === FieldTypes.چندانتخابی) { - initialValues[field.Name] = []; + initialValues[field.NameOrID] = []; } else { - initialValues[field.Name] = ""; + initialValues[field.NameOrID] = ""; } }); setFormValues(initialValues); @@ -39,7 +57,7 @@ const DynamicForm: FC = ({ fields, processId, stepId }) => { ( field: FieldDefinition, value: any, - allValues: Record + allValues: Record ): string | null => { // Evaluate ShowCondition first. If not visible, no validation needed. if ( @@ -141,20 +159,20 @@ const DynamicForm: FC = ({ fields, processId, stepId }) => { ); const handleFieldChange = useCallback( - (fieldName: string, newValue: unknown) => { + (nameOrID: string, newValue: unknown) => { setFormValues((prevValues) => { const updatedValues = { ...prevValues, - [fieldName]: newValue, + [nameOrID]: newValue, }; // Re-validate the field immediately - const fieldDef = fields.find((f) => f.Name === fieldName); + const fieldDef = fields.find((f) => f.NameOrID === nameOrID); if (fieldDef) { const error = validateField(fieldDef, newValue, updatedValues); setFormErrors((prevErrors) => ({ ...prevErrors, - [fieldName]: error, + [nameOrID]: error, })); } return updatedValues; @@ -163,25 +181,7 @@ const DynamicForm: FC = ({ fields, processId, stepId }) => { [fields, validateField] ); - const evaluateShowCondition = useCallback( - (condition: string, currentFormValues: Record): boolean => { - try { - // This is a simplified evaluation. In a real app, you'd use a more robust expression parser. - // Example: "FieldName === 'someValue'" - // For now, we'll just check for simple equality. - const parts = condition.split("===").map((s) => s.trim()); - if (parts.length === 2) { - const fieldName = parts[0]; - const expectedValue = parts[1].replace(/^['"]|['"]$/g, ""); // Remove quotes - return currentFormValues[fieldName] == expectedValue; - } - } catch (e) { - console.error("Error evaluating ShowCondition:", e); - } - return true; // Default to visible if condition cannot be parsed - }, - [] - ); + const visibleFields = useMemo(() => { return fields.filter((field) => { @@ -200,9 +200,9 @@ const DynamicForm: FC = ({ fields, processId, stepId }) => { let hasErrors = false; for (const field of visibleFields) { - const value = formValues[field.Name]; + const value = formValues[field.NameOrID]; const error = validateField(field, value, formValues); // Pass formValues for conditional validation - newErrors[field.Name] = error; + newErrors[field.NameOrID] = error; if (error) { hasErrors = true; } @@ -219,7 +219,29 @@ const DynamicForm: FC = ({ fields, processId, stepId }) => { console.error("Form submission failed:", err); alert("Form submission failed."); } + + const updatedValues: any = { ...formValues }; + for (const [key, value] of Object.entries(formValues)) { + if (value instanceof File && value !== null) { + const fileID = await uploadImage({ + file: value, + name: "", + }); + + updatedValues[key] = fileID.data.data; + } + } + + const filterData = { + ...updatedValues, + run_process: workflowId, + }; + + + if (workflowId) + onChange(filterData) } + setIsSubmitting(false); }; @@ -234,8 +256,8 @@ const DynamicForm: FC = ({ fields, processId, stepId }) => { const colSpanClass = field.FieldStyle_4Col ? "lg:col-span-3" // 4 columns in a 12-column grid : field.FieldStyle_3Col - ? "lg:col-span-4" // 3 columns in a 12-column grid - : "lg:col-span-6"; // Default to 2 columns in a 12-column grid + ? "lg:col-span-4" // 3 columns in a 12-column grid + : "lg:col-span-6"; // Default to 2 columns in a 12-column grid return (
= ({ fields, processId, stepId }) => { - handleFieldChange(field.Name, newValue) + value={formValues[field.NameOrID]} + onChange={(newValue: any) => { + handleFieldChange(field.NameOrID, newValue) + } + } allFormValues={formValues} // Pass all form values for dependent fields /> diff --git a/src/modules/dashboard/pages/step-form/index.tsx b/src/modules/dashboard/pages/step-form/index.tsx index 3730e6a..1dcc0a5 100644 --- a/src/modules/dashboard/pages/step-form/index.tsx +++ b/src/modules/dashboard/pages/step-form/index.tsx @@ -6,134 +6,145 @@ import { FieldTypes } from "@core/utils/dynamic-field.utils"; import { fetchFieldIndex, fetchFielSecondeIndex, + saveFormService, } from "@modules/dashboard/service/dynamic-form.service"; -import { useQuery } from "@tanstack/react-query"; +import { useMutation, useQuery } from "@tanstack/react-query"; import { useSearchParams } from "react-router-dom"; +import { toast } from "react-toastify"; import { getCampaignStepsService } from "../../service/campaigns.service"; -import type { CampaignProcess, StepItems } from "../steps/step.type"; import DynamicForm from "./dynamic-form"; +import type { CampaignProcess, FilterData, GroupedCampaign } from "./step-form.type"; const StepFormPage: FC = () => { const [fields, setFields] = useState([]); const [params] = useSearchParams(); const stageID = params.get("stageID"); const processID = params.get("processID"); + const { data, isLoading, error } = useQuery({ - queryKey: ["dynamic-field", stageID ?? processID], + queryKey: ["dynamic-field", stageID, processID], queryFn: () => { - if (stageID) return fetchFielSecondeIndex(stageID) ; + if (stageID) return fetchFielSecondeIndex(stageID); return fetchFieldIndex(processID!); }, }); - const { data:steps } = useQuery({ - queryKey: ["dynamic-step"], - queryFn: () => getCampaignStepsService(), + const { data: steps } = useQuery({ + queryKey: ["dynamic-step"], + queryFn: () => getCampaignStepsService(), + }); + + + const processedFields = useMemo(() => { + if (!data || !data.Fields) { + return []; + } + + return data.Fields.map((el) => { + const field: any = { + ID: el.ID, + Name: el.Name, + LatinName: el.LatinName, + Type: el.Type, + DefaultValue: el.DefaultValue, + Length: el.Length, + MinValue: el.MinValue, + MaxValue: el.MaxValue, + Option_Options: el.Option_Options, + IsNumeric: el.IsNumeric, + Validation: el.Validation, + ParsedValidation: el.ParsedValidation, + Help: el.Help, + Parent_ProcessID: el.Parent_ProcessID, + Parent_FieldID: el.Parent_FieldID, + FieldStyle_3Col: el.FieldStyle_3Col, + FieldStyle_4Col: el.FieldStyle_4Col, + ShowCondition: el.ShowCondition, + AllowNull: el.AllowNull, + CalculationField_Async: el.CalculationField_Async, + CalculationField_IsFunctionOutput: + el.CalculationField_IsFunctionOutput, + CalculationField_ReCalculateAfterSave: + el.CalculationField_ReCalculateAfterSave, + Calculation_Formula: el.Calculation_Formula, + Calculation_ParsedFormula: el.Calculation_ParsedFormula, + Calculation_ParsedFormulaL2: el.Calculation_ParsedFormulaL2, + Child_OpenAgain: el.Child_OpenAgain, + Child_ProcessID: el.Child_ProcessID, + Child_ShowCalendar: el.Child_ShowCalendar, + Date_Type: el.Date_Type, + Description_HasSaving: el.Description_HasSaving, + Description_IsParser: el.Description_IsParser, + Description_Text: el.Description_Text, + Description: el.Description_Text, + Document: el.Document, + File_Extentions: el.File_Extentions, + File_MaxSize: el.File_MaxSize, + IsDashboardField: el.IsDashboardField, + IsUnique: el.IsUnique, + LockFirstValue: el.LockFirstValue, + NameOrID: el.NameOrID, + Option_IsChips: el.Option_IsChips, + // Option_Options: el.Option_Options, + // Required: el.Required, + OrderNumber: el.OrderNumber, + Parent_CanBeSave: el.Parent_CanBeSave, + Parent_DependentFields: el.Parent_DependentFields, + Parent_Formula: el.Parent_Formula, + Parent_IsMultiSelection: el.Parent_IsMultiSelection, + Parent_IsPersonParent: el.Parent_IsPersonParent, + Parent_LoadAll: el.Parent_LoadAll, + Parent_OpenSearchList: el.Parent_OpenSearchList, + Parent_ParsedFormula: el.Parent_ParsedFormula, + Parent_SelectRemainOption: el.Parent_SelectRemainOption, + Parent_ShowInTree: el.Parent_ShowInTree, + Parent_Type: el.Parent_Type, + ParsedValidationL2: el.ParsedValidationL2, + ShowInChildTable: el.ShowInChildTable, + Status: el.Status, + StepID: el.StepID, + StepName: el.StepName, + TabTitle: el.TabTitle, + Time_HasSecond: el.Time_HasSecond, + Unit: el.Unit, + ViewPermissionCount: el.ViewPermissionCount, + typeValue: el.Type as FieldTypes, + }; + + if (el.Option_Options) { + try { + field.parsedOptions = JSON.parse(el.Option_Options); + } catch (e) { + console.error( + `Error parsing Option_Options for field ${el.ID}:`, + e + ); + field.parsedOptions = []; + } + } + + if (el.ParsedValidation) { + try { + field.parsedValidationRules = JSON.parse(el.ParsedValidation); + } catch (e) { + console.error( + `Error parsing ParsedValidation for field ${el.ID}:`, + e + ); + field.parsedValidationRules = []; + } + } + return field; }); - + }, [data]); useEffect(() => { - if (data && data.Fields) { - const processedFields: LocalFieldDefinition[] = data.Fields.map((el) => { - const field: any = { - ID: el.ID, - Name: el.Name, - LatinName: el.LatinName, - Type: el.Type, - DefaultValue: el.DefaultValue, - Length: el.Length, - MinValue: el.MinValue, - MaxValue: el.MaxValue, - Option_Options: el.Option_Options, - IsNumeric: el.IsNumeric, - Validation: el.Validation, - ParsedValidation: el.ParsedValidation, - Help: el.Help, - Parent_ProcessID: el.Parent_ProcessID, - Parent_FieldID: el.Parent_FieldID, - FieldStyle_3Col: el.FieldStyle_3Col, - FieldStyle_4Col: el.FieldStyle_4Col, - ShowCondition: el.ShowCondition, - AllowNull: el.AllowNull, - CalculationField_Async: el.CalculationField_Async, - CalculationField_IsFunctionOutput: - el.CalculationField_IsFunctionOutput, - CalculationField_ReCalculateAfterSave: - el.CalculationField_ReCalculateAfterSave, - Calculation_Formula: el.Calculation_Formula, - Calculation_ParsedFormula: el.Calculation_ParsedFormula, - Calculation_ParsedFormulaL2: el.Calculation_ParsedFormulaL2, - Child_OpenAgain: el.Child_OpenAgain, - Child_ProcessID: el.Child_ProcessID, - Child_ShowCalendar: el.Child_ShowCalendar, - Date_Type: el.Date_Type, - Description_HasSaving: el.Description_HasSaving, - Description_IsParser: el.Description_IsParser, - Description_Text: el.Description_Text, - Description: el.Description_Text, - Document: el.Document, - File_Extentions: el.File_Extentions, - File_MaxSize: el.File_MaxSize, - IsDashboardField: el.IsDashboardField, - IsUnique: el.IsUnique, - LockFirstValue: el.LockFirstValue, - NameOrID: el.NameOrID, - Option_IsChips: el.Option_IsChips, - // Option_Options: el.Option_Options, - // Required: el.Required, - OrderNumber: el.OrderNumber, - Parent_CanBeSave: el.Parent_CanBeSave, - Parent_DependentFields: el.Parent_DependentFields, - Parent_Formula: el.Parent_Formula, - Parent_IsMultiSelection: el.Parent_IsMultiSelection, - Parent_IsPersonParent: el.Parent_IsPersonParent, - Parent_LoadAll: el.Parent_LoadAll, - Parent_OpenSearchList: el.Parent_OpenSearchList, - Parent_ParsedFormula: el.Parent_ParsedFormula, - Parent_SelectRemainOption: el.Parent_SelectRemainOption, - Parent_ShowInTree: el.Parent_ShowInTree, - Parent_Type: el.Parent_Type, - ParsedValidationL2: el.ParsedValidationL2, - ShowInChildTable: el.ShowInChildTable, - Status: el.Status, - StepID: el.StepID, - StepName: el.StepName, - TabTitle: el.TabTitle, - Time_HasSecond: el.Time_HasSecond, - Unit: el.Unit, - ViewPermissionCount: el.ViewPermissionCount, - typeValue: el.Type as FieldTypes, - }; + setFields(processedFields); + }, [processedFields]); + - if (el.Option_Options) { - try { - field.parsedOptions = JSON.parse(el.Option_Options); - } catch (e) { - console.error( - `Error parsing Option_Options for field ${el.ID}:`, - e - ); - field.parsedOptions = []; - } - } - if (el.ParsedValidation) { - try { - field.parsedValidationRules = JSON.parse(el.ParsedValidation); - } catch (e) { - console.error( - `Error parsing ParsedValidation for field ${el.ID}:`, - e - ); - field.parsedValidationRules = []; - } - } - return field; - }); - setFields(processedFields); - } - }, [data]); const currentStep = useMemo(() => { @@ -141,81 +152,116 @@ const StepFormPage: FC = () => { const row = steps[0]; if (!row || Object.keys(row).length === 0) return []; - const processes = Object.keys(row) - .filter( - (key) => - key.startsWith("process") && - !key.endsWith("_id") && - key.includes("_") - ) - .map((key) => { - const index = key.match(/process(\d+)_/)?.[1]; - if (!index) return null; - return { - processId: row[`process${index}`], - category: row[`process${index}_category`], - score: row[`process${index}_score`], - stageId: row[`process${index}_stage_id`], - status: row[`process${index}_status`], - }; - }) - .filter(Boolean); + // 1) جمع‌آوری فرآیندها + const temp: CampaignProcess[] = []; - // Group + Dedupe - const grouped = Object.values( - processes.reduce( - ( - acc: Record< - string, - { - category: string; - processes: CampaignProcess[]; - stageID: number; - processId: number; - } - >, - item - ) => { - if (!item || !item.category) return acc; + Object.keys(row).forEach(key => { + const match = key.match(/^process(\d+)_category$/); + if (match) { + const i = match[1]; + temp.push({ + processId: String(row[`process${i}`]), + category: String(row[`process${i}_category`]), + score: String(row[`process${i}_score`]), + stageId: String(row[`process${i}_stage_id`]), + status: String(row[`process${i}_status`]), + title: String(row[`process${i}_title`]), + selected: false, + groupIndex: 0, // placeholder + }); + } + }); - if (!acc[item.category]) { - acc[item.category] = { - category: item.category, - processes: [], - stageID: Number(item.stageId), - processId: Number(item.processId), - }; - } + // 2) گروه‌بندی + حذف تکراری + const grouped: GroupedCampaign[] = Object.values( + temp.reduce((acc: Record, item) => { + if (!item?.category) return acc; - // حذف تکراری‌ها بر اساس processId - const exists = acc[item.category].processes.some( - (p) => p.processId === item.processId - ); + if (!acc[item.category]) { + acc[item.category] = { + category: item.category, + processes: [], + groupIndex: 0, + }; + } - if (!exists) { - acc[item.category].processes.push({ - processId: String(item.processId), - category: String(item.category), - score: String(item.score), - stageId: String(item.stageId), - status: String(item.status), - }); - } + const exists = acc[item.category].processes.some( + p => p.processId === item.processId + ); - return acc; - }, - {} - ) + if (!exists) { + acc[item.category].processes.push(item); + } + + return acc; + }, {}) ); - let datas:unknown - if(stageID) datas = grouped.find(el=> el.stageID === Number(stageID)) - else datas = grouped.find(el=> el.processId === Number(processID)) - return datas?.processes as Array; + // 3) ست کردن groupIndex روی گروه‌ها و process های داخلش + grouped.forEach((group, index) => { + group.groupIndex = index; + group.processes.forEach(proc => { + proc.groupIndex = index; + }); + }); + + // 4) فیلتر مناسب بر اساس Stage یا Process + let filtered = grouped; + + if (stageID) { + filtered = grouped.filter(g => g.processes.some(p => p.stageId === String(stageID))); + } + + if (processID) { + filtered = grouped.filter(g => g.processes.some(p => p.processId === String(processID))); + } + + // 5) حالا سلکت داینامیک فقط روی گروه‌های فیلترشده اجرا میشه + filtered.forEach(group => { + const arr = group.processes; + for (let i = 0; i < arr.length; i++) { + if (arr[i].status === "انجام نشده") { + arr[i].selected = true; + break; + } + } + }); + + return filtered.flatMap(g => g.processes); }, [steps, stageID, processID]); + const saveForm = useMutation({ + mutationFn: saveFormService, + onSuccess: (data) => { + if (data.resultType !== 0) { + toast.error(data.message || "خطایی رخ داد"); + return; + } + + toast.success("ثبت نام با موفقیت انجام شد"); + }, + onError: (error: any) => { + console.error("Registration error:", error); + toast.error( + "خطا در ثبت نام: " + (error?.message || "لطفاً دوباره تلاش کنید") + ); + }, + }); + + + const saveFormHandler = (data: FilterData) => { + if (currentStep) { + const changeData = { + [`process${currentStep[0]?.groupIndex + 1}`]: data + } + saveForm.mutate(changeData) + } + } + + + if (isLoading) { return (
@@ -238,17 +284,18 @@ const StepFormPage: FC = () => { فرم پویا
- {currentStep.map((el, idx) => { - return ( -
-
-
{el.category}
-
- ); - })} -
+ {currentStep?.map((el, idx) => { - + return ( +
+
+
{el.title}
+
+ ); + })} +
+ +
); }; diff --git a/src/modules/dashboard/pages/step-form/step-form.type.ts b/src/modules/dashboard/pages/step-form/step-form.type.ts index 2d6498b..139bc23 100644 --- a/src/modules/dashboard/pages/step-form/step-form.type.ts +++ b/src/modules/dashboard/pages/step-form/step-form.type.ts @@ -4,4 +4,29 @@ export interface DynamicFormProps { fields: FieldDefinition[]; processId: number; stepId: number; + workflowId: number + onChange: (items: any) => void } + + +export interface FilterData { + [key: string]: any; + run_process: number; +} + +export type CampaignProcess = { + processId: string; + category: string; + score: string; + stageId: string; + status: string; + title: string; + selected: boolean; + groupIndex: number; +}; + +export type GroupedCampaign = { + category: string; + processes: CampaignProcess[]; + groupIndex: number; +}; \ No newline at end of file diff --git a/src/modules/dashboard/service/dynamic-form.service.ts b/src/modules/dashboard/service/dynamic-form.service.ts index 96f7ade..baa807a 100644 --- a/src/modules/dashboard/service/dynamic-form.service.ts +++ b/src/modules/dashboard/service/dynamic-form.service.ts @@ -3,6 +3,7 @@ import api from "@/core/service/axios"; import type { WorkflowResponse } from "@/core/types/global.type"; import to from "await-to-js"; import { toast } from "react-toastify"; +import type { FilterData } from "../pages/step-form/step-form.type"; export const fetchFieldIndex = async ( id: string @@ -23,7 +24,7 @@ export const fetchFieldIndex = async ( export const fetchFielSecondeIndex = async ( id: string ): Promise => { - const [err, res] = await to(api.post(API_ADDRESS.index, id)); + const [err, res] = await to(api.post(API_ADDRESS.index2, id)); if (err) { throw err; } @@ -35,3 +36,18 @@ export const fetchFielSecondeIndex = async ( const data = JSON.parse(res.data.data); return data; }; + + +export const saveFormService = async (items: FilterData) => { + const [err, res] = await to(api.post(API_ADDRESS.save, items)); + if (err) { + throw err; + } + + if (res.data.resultType !== 0) { + toast.error(res.data.message || "خطا در دریافت فیلدها"); + throw new Error("خطا در دریافت فیلدها"); + } + const data = JSON.parse(res.data.data); + return data; +} \ No newline at end of file