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.
This commit is contained in:
parent
518650ccd5
commit
7c8590c075
|
|
@ -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<FormFieldProps> = ({
|
|||
}) => {
|
||||
const [error, setError] = useState<string | null>(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<FormFieldProps> = ({
|
|||
setError(errorMessage);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
validateField(value);
|
||||
}, [value]);
|
||||
|
||||
const handleChange = (
|
||||
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
|
||||
) => {
|
||||
|
|
@ -152,8 +155,7 @@ const FormField: FC<FormFieldProps> = ({
|
|||
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",
|
||||
};
|
||||
|
|
@ -251,6 +253,7 @@ const FormField: FC<FormFieldProps> = ({
|
|||
/>
|
||||
);
|
||||
case "select":
|
||||
{
|
||||
const isDependent = field.Parent_FieldID !== undefined;
|
||||
const parentFieldValue =
|
||||
isDependent && field.Parent_FieldID !== undefined
|
||||
|
|
@ -281,13 +284,13 @@ const FormField: FC<FormFieldProps> = ({
|
|||
undefined
|
||||
}
|
||||
onChange={(optionValue: string) => onChange(optionValue)}
|
||||
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"
|
||||
}`}
|
||||
fetchOptions={fetchDependentOptions}
|
||||
disabled={isDependent && !parentFieldValue} // Disable if dependent and parent value is not set
|
||||
/>
|
||||
);
|
||||
}
|
||||
case "radio":
|
||||
return (
|
||||
<div className="flex flex-col space-y-2" dir="rtl">
|
||||
|
|
@ -325,19 +328,17 @@ const FormField: FC<FormFieldProps> = ({
|
|||
<ImageUploader
|
||||
onImageChange={(file: File | null) => 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 (
|
||||
<CustomInput
|
||||
<FileUploader
|
||||
{...commonProps}
|
||||
type="file"
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
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<FormFieldProps> = ({
|
|||
{field.Description || field.Name}
|
||||
</p>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<CustomInput
|
||||
{...commonProps}
|
||||
type="text"
|
||||
maxLength={field.Length!}
|
||||
placeholder={field.Name}
|
||||
/>
|
||||
);
|
||||
// default:
|
||||
// return (
|
||||
// <CustomInput
|
||||
// {...commonProps}
|
||||
// type="text"
|
||||
// maxLength={field.Length!}
|
||||
// placeholder={field.Name}
|
||||
// />
|
||||
// );
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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("توکن یافت نشد");
|
||||
|
|
|
|||
|
|
@ -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<DynamicFormProps> = ({ fields, processId, stepId }) => {
|
||||
const [formValues, setFormValues] = useState<Record<string, any>>({});
|
||||
const DynamicForm: FC<DynamicFormProps> = ({ fields, processId, stepId, workflowId, onChange }) => {
|
||||
const [formValues, setFormValues] = useState({});
|
||||
const [_, setFormErrors] = useState<Record<string, string | null>>({});
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const evaluateShowCondition = useCallback(
|
||||
(condition: string, currentFormValues: Record<string, any>): 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<string, any> = {};
|
||||
const initialValues: Record<string, unknown> = {};
|
||||
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<DynamicFormProps> = ({ fields, processId, stepId }) => {
|
|||
(
|
||||
field: FieldDefinition,
|
||||
value: any,
|
||||
allValues: Record<string, any>
|
||||
allValues: Record<string, unknown>
|
||||
): string | null => {
|
||||
// Evaluate ShowCondition first. If not visible, no validation needed.
|
||||
if (
|
||||
|
|
@ -141,20 +159,20 @@ const DynamicForm: FC<DynamicFormProps> = ({ 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<DynamicFormProps> = ({ fields, processId, stepId }) => {
|
|||
[fields, validateField]
|
||||
);
|
||||
|
||||
const evaluateShowCondition = useCallback(
|
||||
(condition: string, currentFormValues: Record<string, any>): 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<DynamicFormProps> = ({ 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<DynamicFormProps> = ({ 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);
|
||||
};
|
||||
|
||||
|
|
@ -248,9 +270,11 @@ const DynamicForm: FC<DynamicFormProps> = ({ fields, processId, stepId }) => {
|
|||
</label>
|
||||
<FormField
|
||||
field={field}
|
||||
value={formValues[field.Name]}
|
||||
onChange={(newValue: any) =>
|
||||
handleFieldChange(field.Name, newValue)
|
||||
value={formValues[field.NameOrID]}
|
||||
onChange={(newValue: any) => {
|
||||
handleFieldChange(field.NameOrID, newValue)
|
||||
}
|
||||
|
||||
}
|
||||
allFormValues={formValues} // Pass all form values for dependent fields
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -6,20 +6,23 @@ 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<LocalFieldDefinition[]>([]);
|
||||
const [params] = useSearchParams();
|
||||
const stageID = params.get("stageID");
|
||||
const processID = params.get("processID");
|
||||
|
||||
const { data, isLoading, error } = useQuery<WorkflowResponse>({
|
||||
queryKey: ["dynamic-field", stageID ?? processID],
|
||||
queryKey: ["dynamic-field", stageID, processID],
|
||||
queryFn: () => {
|
||||
if (stageID) return fetchFielSecondeIndex(stageID);
|
||||
return fetchFieldIndex(processID!);
|
||||
|
|
@ -33,9 +36,12 @@ const StepFormPage: FC = () => {
|
|||
});
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (data && data.Fields) {
|
||||
const processedFields: LocalFieldDefinition[] = data.Fields.map((el) => {
|
||||
const processedFields = useMemo(() => {
|
||||
if (!data || !data.Fields) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return data.Fields.map((el) => {
|
||||
const field: any = {
|
||||
ID: el.ID,
|
||||
Name: el.Name,
|
||||
|
|
@ -131,91 +137,131 @@ const StepFormPage: FC = () => {
|
|||
}
|
||||
return field;
|
||||
});
|
||||
setFields(processedFields);
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
setFields(processedFields);
|
||||
}, [processedFields]);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const currentStep = useMemo(() => {
|
||||
if (!Array.isArray(steps) || steps.length === 0) return [];
|
||||
|
||||
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;
|
||||
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
|
||||
});
|
||||
}
|
||||
>,
|
||||
item
|
||||
) => {
|
||||
if (!item || !item.category) return acc;
|
||||
});
|
||||
|
||||
// 2) گروهبندی + حذف تکراری
|
||||
const grouped: GroupedCampaign[] = Object.values(
|
||||
temp.reduce((acc: Record<string, GroupedCampaign>, item) => {
|
||||
if (!item?.category) return acc;
|
||||
|
||||
if (!acc[item.category]) {
|
||||
acc[item.category] = {
|
||||
category: item.category,
|
||||
processes: [],
|
||||
stageID: Number(item.stageId),
|
||||
processId: Number(item.processId),
|
||||
groupIndex: 0,
|
||||
};
|
||||
}
|
||||
|
||||
// حذف تکراریها بر اساس processId
|
||||
const exists = acc[item.category].processes.some(
|
||||
(p) => p.processId === item.processId
|
||||
p => p.processId === item.processId
|
||||
);
|
||||
|
||||
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),
|
||||
});
|
||||
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<StepItems>;
|
||||
// 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 (
|
||||
<div className="container mx-auto p-4 text-center font-vazir">
|
||||
|
|
@ -238,17 +284,18 @@ const StepFormPage: FC = () => {
|
|||
فرم پویا
|
||||
</h1>
|
||||
<div className="flex flex-row justify-center gap-10">
|
||||
{currentStep.map((el, idx) => {
|
||||
{currentStep?.map((el, idx) => {
|
||||
|
||||
return (
|
||||
<div key={idx} className="flex flex-col justify-center mb-4 text-right">
|
||||
<div className="w-7 h-7 bg-red-950 rounded-full block m-auto"></div>
|
||||
<div className="font-semibold text-sm mb-2">{el.category}</div>
|
||||
<div className={`w-7 h-7 bg-red-950 rounded-full block m-auto ${el.selected ? 'bg-green-700!' : ''}`}></div>
|
||||
<div className="font-semibold text-sm mb-2">{el.title}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<DynamicForm fields={fields} processId={1} stepId={1} />
|
||||
<DynamicForm fields={fields} processId={1} stepId={1} workflowId={steps[0]?.WorkflowID} onChange={saveFormHandler} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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<WorkflowResponse> => {
|
||||
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;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user