@@ -145,7 +145,7 @@ function TaskListComponent({
|
||||
key: "priority",
|
||||
sorter: true,
|
||||
sortOrder: sortcolumn === "priority" && sortorder,
|
||||
width: '5%',
|
||||
width: '8%',
|
||||
render: (text, record) => <PriorityLabel priority={record.priority}
|
||||
/>
|
||||
},
|
||||
@@ -157,7 +157,6 @@ function TaskListComponent({
|
||||
<Space direction='horizontal'>
|
||||
<Button title={t('tasks.buttons.edit')} onClick={() => {
|
||||
setTaskUpsertContext({
|
||||
actions: {},
|
||||
context: {
|
||||
existingTask: record,
|
||||
},
|
||||
|
||||
@@ -2,15 +2,19 @@ import queryString from "query-string";
|
||||
import {useLocation} from "react-router-dom";
|
||||
import {useMutation, useQuery} from "@apollo/client";
|
||||
import {
|
||||
MUTATION_TOGGLE_TASK_COMPLETED, MUTATION_TOGGLE_TASK_DELETED,
|
||||
MUTATION_TOGGLE_TASK_COMPLETED,
|
||||
MUTATION_TOGGLE_TASK_DELETED,
|
||||
QUERY_MY_TASKS_PAGINATED
|
||||
} from "../../graphql/tasks.queries.js";
|
||||
import {pageLimit} from "../../utils/config.js";
|
||||
import AlertComponent from "../alert/alert.component.jsx";
|
||||
import React, {useEffect} from "react";
|
||||
import TaskListComponent from "./task-list.component.jsx";
|
||||
import {notification} from "antd";
|
||||
import {useTranslation} from "react-i18next";
|
||||
|
||||
export default function TaskListContainer({bodyshop, currentUser}) {
|
||||
const {t} = useTranslation();
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const {page, sortcolumn, sortorder, deleted, completed} = searchParams;
|
||||
const {loading, error, data, refetch} = useQuery(
|
||||
@@ -45,7 +49,8 @@ export default function TaskListContainer({bodyshop, currentUser}) {
|
||||
const handleTaskUpdated = () => {
|
||||
refetch().catch((e) => {
|
||||
console.error(`Something went wrong fetching tasks: ${e.message || ''}`);
|
||||
}); };
|
||||
});
|
||||
};
|
||||
}, [refetch]);
|
||||
|
||||
/**
|
||||
@@ -61,16 +66,26 @@ export default function TaskListContainer({bodyshop, currentUser}) {
|
||||
*/
|
||||
const toggleCompletedStatus = async (id, currentStatus) => {
|
||||
const completed_at = !currentStatus ? new Date().toISOString() : null;
|
||||
await toggleTaskCompleted({
|
||||
variables: {
|
||||
id: id,
|
||||
completed: !currentStatus,
|
||||
completed_at: completed_at
|
||||
}
|
||||
});
|
||||
refetch().catch((e) => {
|
||||
console.error(`Something went wrong fetching tasks: ${e.message || ''}`);
|
||||
}); };
|
||||
try {
|
||||
await toggleTaskCompleted({
|
||||
variables: {
|
||||
id: id,
|
||||
completed: !currentStatus,
|
||||
completed_at: completed_at
|
||||
}
|
||||
});
|
||||
refetch().catch((e) => {
|
||||
console.error(`Something went wrong fetching tasks: ${e.message || ''}`);
|
||||
});
|
||||
notification["success"]({
|
||||
message: t("tasks.successes.completed"),
|
||||
});
|
||||
} catch (err) {
|
||||
notification["error"]({
|
||||
message: t("tasks.failures.completed"),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle task deleted mutation
|
||||
@@ -85,20 +100,30 @@ export default function TaskListContainer({bodyshop, currentUser}) {
|
||||
*/
|
||||
const toggleDeletedStatus = async (id, currentStatus) => {
|
||||
const deleted_at = !currentStatus ? new Date().toISOString() : null;
|
||||
await toggleTaskDeleted({
|
||||
variables: {
|
||||
id: id,
|
||||
deleted: !currentStatus,
|
||||
deleted_at: deleted_at
|
||||
}
|
||||
});
|
||||
refetch().catch((e) => {
|
||||
console.error(`Something went wrong fetching tasks: ${e.message || ''}`);
|
||||
});
|
||||
try {
|
||||
await toggleTaskDeleted({
|
||||
variables: {
|
||||
id: id,
|
||||
deleted: !currentStatus,
|
||||
deleted_at: deleted_at
|
||||
}
|
||||
});
|
||||
refetch().catch((e) => {
|
||||
console.error(`Something went wrong fetching tasks: ${e.message || ''}`);
|
||||
});
|
||||
notification["success"]({
|
||||
message: t("tasks.successes.deleted"),
|
||||
});
|
||||
} catch (err) {
|
||||
notification["error"]({
|
||||
message: t("tasks.failures.deleted"),
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
|
||||
return (
|
||||
<TaskListComponent
|
||||
loading={loading}
|
||||
|
||||
@@ -8,6 +8,7 @@ import dayjs from '../../utils/day';
|
||||
|
||||
import {connect} from "react-redux";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component.jsx";
|
||||
import JobSearchSelectComponent from "../job-search-select/job-search-select.component.jsx";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -41,8 +42,27 @@ export function TaskUpsertModalComponent({
|
||||
{label: t('tasks.date_presets.three_weeks'), value: dayjs().add(3, 'weeks')},
|
||||
{label: t('tasks.date_presets.one_month'), value: dayjs().add(1, 'month')},
|
||||
];
|
||||
|
||||
if (loading || error) return <LoadingSkeleton active/>;
|
||||
|
||||
const clearRelations = () => {
|
||||
form.setFieldsValue({
|
||||
billid: null,
|
||||
partsorderid: null,
|
||||
joblineid: null
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the selected job id
|
||||
* @param jobId
|
||||
*/
|
||||
const changeJobId = (jobId) => {
|
||||
setSelectedJobId(jobId || null);
|
||||
// Reset the form fields when selectedJobId changes
|
||||
clearRelations();
|
||||
};
|
||||
|
||||
|
||||
if (!data || loading || error) return <LoadingSkeleton active/>;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -80,20 +100,95 @@ export function TaskUpsertModalComponent({
|
||||
label={t("tasks.fields.completed")}
|
||||
name="completed"
|
||||
valuePropName="checked"
|
||||
initialValue={false}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Switch/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<Form.Item
|
||||
name="jobid"
|
||||
// initialValue={selectedJobId}
|
||||
label={t("tasks.fields.jobid")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<JobSearchSelectComponent placeholder={t('tasks.placeholders.jobid')}
|
||||
onSelect={changeJobId} onClear={changeJobId}/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
label={t("tasks.fields.joblineid")}
|
||||
name="joblineid"
|
||||
>
|
||||
<Select allowClear placeholder={t("tasks.placeholders.joblineid")}
|
||||
disabled={!selectedJobDetails || !selectedJobId}>
|
||||
{selectedJobDetails?.joblines?.map((jobline) => (
|
||||
<Select.Option key={jobline.id} value={jobline.id}>
|
||||
{jobline.line_desc}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
label={t("tasks.fields.partsorderid")}
|
||||
name="partsorderid"
|
||||
>
|
||||
<Select allowClear placeholder={t("tasks.placeholders.partsorderid")}
|
||||
disabled={!selectedJobDetails || !selectedJobId}>
|
||||
{selectedJobDetails?.parts_orders?.map((partsOrder) => (
|
||||
<Select.Option key={partsOrder.id} value={partsOrder.id}>
|
||||
{partsOrder.order_number} - {partsOrder.vendor.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
label={t("tasks.fields.billid")}
|
||||
name="billid"
|
||||
>
|
||||
<Select allowClear placeholder={t("tasks.placeholders.billid")}
|
||||
disabled={!selectedJobDetails || !selectedJobId}>
|
||||
{selectedJobDetails?.bills?.map((bill) => (
|
||||
<Select.Option key={bill.id} value={bill.id}>
|
||||
{bill.invoice_number} - {bill.vendor.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
label={t("tasks.fields.assigned_to")}
|
||||
name="assigned_to"
|
||||
initialValue={currentUser.email}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select placeholder={t("tasks.labels.selectemployee")}>
|
||||
{bodyshop.employees.map((employee) => (
|
||||
{bodyshop.employees.filter(x => x.active).map((employee) => (
|
||||
<Select.Option key={employee.id} value={employee.user_email}>
|
||||
{employee.first_name} {employee.last_name}
|
||||
</Select.Option>
|
||||
@@ -131,71 +226,6 @@ export function TaskUpsertModalComponent({
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<Form.Item
|
||||
name="jobid"
|
||||
label={t("tasks.fields.jobid")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},1
|
||||
]}
|
||||
>
|
||||
<Select placeholder={t('tasks.placeholders.jobid')} defaultValue={selectedJobId} onSelect={setSelectedJobId}>
|
||||
{data.jobs.map((job) => (
|
||||
<Select.Option key={job.id} value={job.id}>
|
||||
{job.ro_number}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
label={t("tasks.fields.joblineid")}
|
||||
name="joblineid"
|
||||
>
|
||||
<Select placeholder={t("tasks.placeholders.joblineid")} disabled={!selectedJobDetails}>
|
||||
{selectedJobDetails?.joblines?.map((jobline) => (
|
||||
<Select.Option key={jobline.id} value={jobline.id}>
|
||||
{jobline.line_desc}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
label={t("tasks.fields.partsorderid")}
|
||||
name="partsorderid"
|
||||
>
|
||||
<Select placeholder={t("tasks.placeholders.partsorderid")} disabled={!selectedJobDetails}>
|
||||
{selectedJobDetails?.parts_orders?.map((partsOrder) => (
|
||||
<Select.Option key={partsOrder.id} value={partsOrder.id}>
|
||||
{partsOrder.order_number} - {partsOrder.vendor.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
label={t("tasks.fields.billid")}
|
||||
name="billid"
|
||||
>
|
||||
<Select placeholder={t("tasks.placeholders.billid")} disabled={!selectedJobDetails}>
|
||||
{selectedJobDetails?.bills?.map((bill) => (
|
||||
<Select.Option key={bill.id} value={bill.id}>
|
||||
{bill.invoice_number} - {bill.vendor.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,14 +5,12 @@ import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {MUTATION_INSERT_NEW_TASK, MUTATION_UPDATE_TASK} from "../../graphql/tasks.queries";
|
||||
import {
|
||||
QUERY_GET_TASKS_JOB_DETAILS,
|
||||
QUERY_GET_TASKS_JOB_DETAILS_BY_ID
|
||||
} from "../../graphql/jobs.queries.js";
|
||||
import {QUERY_GET_TASKS_JOB_DETAILS_BY_ID} from "../../graphql/jobs.queries.js";
|
||||
import {toggleModalVisible} from "../../redux/modals/modals.actions";
|
||||
import {selectTaskUpsert} from "../../redux/modals/modals.selectors";
|
||||
import {selectBodyshop, selectCurrentUser} from "../../redux/user/user.selectors";
|
||||
import TaskUpsertModalComponent from "./task-upsert-modal.component";
|
||||
import {replaceUndefinedWithNull} from "../../utils/undefinedtonull.js";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
@@ -41,16 +39,14 @@ export function TaskUpsertModalContainer({
|
||||
const [selectedJobDetails, setSelectedJobDetails] = useState(null);
|
||||
const [jobIdState, setJobIdState] = useState(null);
|
||||
|
||||
|
||||
const {
|
||||
loading: jobDetailsLoading,
|
||||
error: jobDetailsError,
|
||||
data: jobDetailsData
|
||||
loading: loading,
|
||||
error: error,
|
||||
data: data
|
||||
} = useQuery(QUERY_GET_TASKS_JOB_DETAILS_BY_ID, {
|
||||
variables: {id: jobIdState},
|
||||
skip: !jobIdState, // Skip the query if jobIdState is null
|
||||
skip: !jobIdState,
|
||||
});
|
||||
const {loading, error, data} = useQuery(QUERY_GET_TASKS_JOB_DETAILS);
|
||||
|
||||
/**
|
||||
* Set the selected job id when the modal is opened and jobId is passed as a prop or when an existing task is passed as a prop
|
||||
@@ -72,16 +68,6 @@ export function TaskUpsertModalContainer({
|
||||
}
|
||||
}, [existingTask, form, open]);
|
||||
|
||||
/**
|
||||
* Reset the form values when the selected job id changes
|
||||
*/
|
||||
useEffect(() => {
|
||||
form.setFieldsValue({
|
||||
joblineid: undefined,
|
||||
billid: undefined,
|
||||
partsorderid: undefined,
|
||||
});
|
||||
}, [selectedJobId, form]);
|
||||
|
||||
/**
|
||||
* Set the job id state when the selected job id changes
|
||||
@@ -97,10 +83,11 @@ export function TaskUpsertModalContainer({
|
||||
* Set the selected job details when the job details query is successful
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!jobDetailsLoading && !jobDetailsError && jobDetailsData) {
|
||||
setSelectedJobDetails(jobDetailsData.jobs_by_pk);
|
||||
if (!loading && !error && data) {
|
||||
setSelectedJobDetails(data.jobs_by_pk);
|
||||
}
|
||||
}, [jobDetailsLoading, jobDetailsError, jobDetailsData]);
|
||||
}, [loading, error, data]);
|
||||
|
||||
|
||||
/**
|
||||
* Handle the form submit
|
||||
@@ -109,13 +96,11 @@ export function TaskUpsertModalContainer({
|
||||
*/
|
||||
const handleFinish = async (formValues) => {
|
||||
const {...values} = formValues;
|
||||
|
||||
if (existingTask) {
|
||||
await updateTask({
|
||||
variables: {
|
||||
taskId: existingTask.id,
|
||||
task: values,
|
||||
jobid: selectedJobId,
|
||||
task: replaceUndefinedWithNull(values)
|
||||
},
|
||||
});
|
||||
|
||||
@@ -128,19 +113,19 @@ export function TaskUpsertModalContainer({
|
||||
await insertTask({
|
||||
variables: {
|
||||
taskInput: [
|
||||
{...values, jobid: selectedJobId, created_by: currentUser.email, bodyshopid: bodyshop.id},
|
||||
{
|
||||
...replaceUndefinedWithNull(values),
|
||||
created_by: currentUser.email,
|
||||
bodyshopid: bodyshop.id
|
||||
},
|
||||
],
|
||||
},
|
||||
updateQueries: {
|
||||
query: QUERY_GET_TASKS_JOB_DETAILS,
|
||||
},
|
||||
update(cache) {
|
||||
cache.modify({
|
||||
fields: {
|
||||
tasks(existingTasks) {
|
||||
return [{
|
||||
...values,
|
||||
jobid: selectedJobId,
|
||||
created_by: currentUser.email,
|
||||
bodyshopid: bodyshop.id
|
||||
}, ...existingTasks]
|
||||
@@ -154,7 +139,7 @@ export function TaskUpsertModalContainer({
|
||||
form.resetFields();
|
||||
toggleModalVisible();
|
||||
notification["success"]({
|
||||
message: t("tasks.successes.create"),
|
||||
message: t("tasks.successes.created"),
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -176,7 +161,7 @@ export function TaskUpsertModalContainer({
|
||||
>
|
||||
<Form form={form} onFinish={handleFinish} layout="vertical">
|
||||
<TaskUpsertModalComponent form={form} loading={loading} error={error} data={data}
|
||||
selectedJobId={setSelectedJobId}
|
||||
selectedJobId={selectedJobId}
|
||||
setSelectedJobId={setSelectedJobId}
|
||||
selectedJobDetails={selectedJobDetails}/>
|
||||
|
||||
|
||||
@@ -2156,18 +2156,6 @@ export const QUERY_GET_TASKS_JOB_DETAILS_BY_ID = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_GET_TASKS_JOB_DETAILS = gql`
|
||||
query GetTasksJobDetails {
|
||||
jobs {
|
||||
id
|
||||
ro_number
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_co_nm
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_JOB_FOR_CC_CONTRACT = gql`
|
||||
query GET_JOB_FOR_CC_CONTRACT($id: uuid!) {
|
||||
jobs_by_pk(id: $id) {
|
||||
|
||||
@@ -900,6 +900,7 @@
|
||||
},
|
||||
"titles": {
|
||||
"joblifecycle": "Job Life Cycles",
|
||||
"tasks": "Tasks",
|
||||
"labhours": "Total Body Hours",
|
||||
"larhours": "Total Refinish Hours",
|
||||
"monthlyemployeeefficiency": "Monthly Employee Efficiency",
|
||||
@@ -2096,6 +2097,18 @@
|
||||
}
|
||||
},
|
||||
"tasks": {
|
||||
"failures": {
|
||||
"created": "Failed to create Task.",
|
||||
"deleted": "Failed to toggle Task deletion.",
|
||||
"updated": "Failed to update Task.",
|
||||
"completed": "Failed to toggle Task completion."
|
||||
},
|
||||
"successes": {
|
||||
"created": "Task created successfully.",
|
||||
"deleted": "Toggled Task deletion successfully.",
|
||||
"updated": "Task updated successfully.",
|
||||
"completed": "Toggled Task completion successfully."
|
||||
},
|
||||
"date_presets": {
|
||||
"next_week": "Next Week",
|
||||
"two_weeks": "Two Weeks",
|
||||
@@ -3117,6 +3130,7 @@
|
||||
"accounting-receivables": "Receivables | {{app}}",
|
||||
"app": "",
|
||||
"bc": {
|
||||
"tasks": "Tasks",
|
||||
"accounting-payables": "Payables",
|
||||
"accounting-payments": "Payments",
|
||||
"accounting-receivables": "Receivables",
|
||||
|
||||
@@ -900,6 +900,7 @@
|
||||
},
|
||||
"titles": {
|
||||
"joblifecycle": "",
|
||||
"tasks": "",
|
||||
"labhours": "",
|
||||
"larhours": "",
|
||||
"monthlyemployeeefficiency": "",
|
||||
@@ -2095,6 +2096,18 @@
|
||||
}
|
||||
},
|
||||
"tasks": {
|
||||
"failures": {
|
||||
"created": "",
|
||||
"deleted": "",
|
||||
"updated": "",
|
||||
"completed": ""
|
||||
},
|
||||
"successes": {
|
||||
"created": "",
|
||||
"deleted": "",
|
||||
"updated": "",
|
||||
"completed": ""
|
||||
},
|
||||
"date_presets": {
|
||||
"next_week": "",
|
||||
"two_weeks": "",
|
||||
@@ -3116,6 +3129,7 @@
|
||||
"accounting-receivables": "",
|
||||
"app": "",
|
||||
"bc": {
|
||||
"tasks": "",
|
||||
"accounting-payables": "",
|
||||
"accounting-payments": "",
|
||||
"accounting-receivables": "",
|
||||
|
||||
@@ -900,7 +900,8 @@
|
||||
},
|
||||
"titles": {
|
||||
"joblifecycle": "",
|
||||
"labhours": "",
|
||||
"tasks": "",
|
||||
"labhours": "",
|
||||
"larhours": "",
|
||||
"monthlyemployeeefficiency": "",
|
||||
"monthlyjobcosting": "",
|
||||
@@ -2095,6 +2096,18 @@
|
||||
}
|
||||
},
|
||||
"tasks": {
|
||||
"failures": {
|
||||
"created": "",
|
||||
"deleted": "",
|
||||
"updated": "",
|
||||
"completed": ""
|
||||
},
|
||||
"successes": {
|
||||
"created": "",
|
||||
"deleted": "",
|
||||
"updated": "",
|
||||
"completed": ""
|
||||
},
|
||||
"date_presets": {
|
||||
"next_week": "",
|
||||
"two_weeks": "",
|
||||
@@ -3116,7 +3129,8 @@
|
||||
"accounting-receivables": "",
|
||||
"app": "",
|
||||
"bc": {
|
||||
"accounting-payables": "",
|
||||
"tasks": "",
|
||||
"accounting-payables": "",
|
||||
"accounting-payments": "",
|
||||
"accounting-receivables": "",
|
||||
"availablejobs": "",
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/**
|
||||
* Replaces undefined values with null in an object.
|
||||
* Optionally, you can specify keys to replace.
|
||||
* If keys are specified, only those keys will be replaced.
|
||||
* If no keys are specified, all undefined values will be replaced.
|
||||
* @param obj
|
||||
* @param keys
|
||||
* @returns {*}
|
||||
* @constructor
|
||||
*/
|
||||
export default function UndefinedToNull(obj, keys) {
|
||||
Object.keys(obj).forEach((key) => {
|
||||
if (keys && keys.indexOf(key) >= 0) {
|
||||
@@ -8,3 +18,21 @@ export default function UndefinedToNull(obj, keys) {
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces undefined values with null in an object. Optionally, you can specify keys to replace. If keys are specified, only those keys will be replaced. If no keys are specified, all undefined values will be replaced.
|
||||
* @param obj
|
||||
* @param keys
|
||||
* @returns {{[p: string]: unknown}}
|
||||
*/
|
||||
export function replaceUndefinedWithNull(obj, keys) {
|
||||
return Object.fromEntries(
|
||||
Object.entries(obj).map(([key, value]) => {
|
||||
if (keys) {
|
||||
return [key, (keys.includes(key) && value === undefined) ? null : value];
|
||||
} else {
|
||||
return [key, value === undefined ? null : value];
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user