Reformat all project files to use the prettier config file.

This commit is contained in:
Patrick Fic
2024-03-27 15:35:07 -07:00
parent b161530381
commit e1df64d592
873 changed files with 111387 additions and 125473 deletions

View File

@@ -1,22 +1,22 @@
import {Button, Card, Checkbox, Col, Form, Input, InputNumber, Row, Select} from "antd";
import React, {useCallback, useEffect, useMemo, useState} from "react";
import {fetchFilterData} from "../../utils/RenderTemplate";
import {DeleteFilled} from "@ant-design/icons";
import {useTranslation} from "react-i18next";
import {getOrderOperatorsByType, getWhereOperatorsByType} from "../../utils/graphQLmodifier";
import { Button, Card, Checkbox, Col, Form, Input, InputNumber, Row, Select } from "antd";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { fetchFilterData } from "../../utils/RenderTemplate";
import { DeleteFilled } from "@ant-design/icons";
import { useTranslation } from "react-i18next";
import { getOrderOperatorsByType, getWhereOperatorsByType } from "../../utils/graphQLmodifier";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import {generateInternalReflections} from "./report-center-modal-utils";
import {FormDatePicker} from "../form-date-picker/form-date-picker.component.jsx";
import { generateInternalReflections } from "./report-center-modal-utils";
import { FormDatePicker } from "../form-date-picker/form-date-picker.component.jsx";
export default function ReportCenterModalFiltersSortersComponent({form, bodyshop}) {
return (
<Form.Item style={{margin: 0, padding: 0}} dependencies={["key"]}>
{() => {
const key = form.getFieldValue("key");
return <RenderFilters form={form} templateId={key} bodyshop={bodyshop}/>;
}}
</Form.Item>
);
export default function ReportCenterModalFiltersSortersComponent({ form, bodyshop }) {
return (
<Form.Item style={{ margin: 0, padding: 0 }} dependencies={["key"]}>
{() => {
const key = form.getFieldValue("key");
return <RenderFilters form={form} templateId={key} bodyshop={bodyshop} />;
}}
</Form.Item>
);
}
/**
@@ -27,236 +27,242 @@ export default function ReportCenterModalFiltersSortersComponent({form, bodyshop
* @returns {JSX.Element}
* @constructor
*/
function FiltersSection({filters, form, bodyshop}) {
const {t} = useTranslation();
function FiltersSection({ filters, form, bodyshop }) {
const { t } = useTranslation();
return (
<Card type='inner' title={t('reportcenter.labels.advanced_filters_filters')} style={{marginTop: '10px'}}>
<Form.List name={["filters"]}>
{(fields, {add, remove}) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<Row gutter={[16, 16]}>
<Col span={10}>
<Form.Item
key={`${index}field`}
label={t('reportcenter.labels.advanced_filters_filter_field')}
name={[field.name, "field"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select
getPopupContainer={trigger => trigger.parentNode}
onChange={() => {
// Clear related Fields
form.setFieldValue(['filters', field.name, 'value'], null);
form.setFieldValue(['filters', field.name, 'operator'], null);
}}
options={
filters.map((f) => {
return {
value: f.name,
label: f?.translation ? (t(f.translation) === f.translation ? f.label : t(f.translation)) : f.label,
}
})
}
/>
</Form.Item>
</Col>
<Col span={6}>
<Form.Item
dependencies={[['filters', field.name, "field"],['filters', field.name, "value"]]}
>
{
() => {
const name = form.getFieldValue(['filters', field.name, "field"]);
const type = filters.find(f => f.name === name)?.type;
return (
<Card type="inner" title={t("reportcenter.labels.advanced_filters_filters")} style={{ marginTop: "10px" }}>
<Form.List name={["filters"]}>
{(fields, { add, remove }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<Row gutter={[16, 16]}>
<Col span={10}>
<Form.Item
key={`${index}field`}
label={t("reportcenter.labels.advanced_filters_filter_field")}
name={[field.name, "field"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Select
getPopupContainer={(trigger) => trigger.parentNode}
onChange={() => {
// Clear related Fields
form.setFieldValue(["filters", field.name, "value"], null);
form.setFieldValue(["filters", field.name, "operator"], null);
}}
options={filters.map((f) => {
return {
value: f.name,
label: f?.translation
? t(f.translation) === f.translation
? f.label
: t(f.translation)
: f.label
};
})}
/>
</Form.Item>
</Col>
<Col span={6}>
<Form.Item
dependencies={[
["filters", field.name, "field"],
["filters", field.name, "value"]
]}
>
{() => {
const name = form.getFieldValue(["filters", field.name, "field"]);
const type = filters.find((f) => f.name === name)?.type;
return <Form.Item
key={`${index}operator`}
label={t('reportcenter.labels.advanced_filters_filter_operator')}
name={[field.name, "operator"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select
getPopupContainer={trigger => trigger.parentNode}
options={ getWhereOperatorsByType(type)}
onChange={() => {
// Clear related Fields
return (
<Form.Item
key={`${index}operator`}
label={t("reportcenter.labels.advanced_filters_filter_operator")}
name={[field.name, "operator"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Select
getPopupContainer={(trigger) => trigger.parentNode}
options={getWhereOperatorsByType(type)}
onChange={() => {
// Clear related Fields
form.setFieldValue(['filters', field.name, 'value'], undefined);
}}
/>
</Form.Item>
}
}
</Form.Item>
</Col>
<Col span={6}>
<Form.Item dependencies={[
['filters', field.name, "field"],
['filters', field.name, "operator"]
]}
>
{
() => {
// Because it looks cleaner than inlining.
const name = form.getFieldValue(['filters', field.name, "field"]);
const type = filters.find(f => f.name === name)?.type;
const reflector = filters.find(f => f.name === name)?.reflector;
const operator = form.getFieldValue(['filters', field.name, "operator"]);
const operatorType = operator ? getWhereOperatorsByType(type).find((o) => o.value === operator)?.type : null;
return <Form.Item
key={`${index}value`}
label={t('reportcenter.labels.advanced_filters_filter_value')}
name={[field.name, "value"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
{
(() => {
const generateReflections = (reflector) => {
if (!reflector) return [];
const {name} = reflector;
const path = name?.split('.');
const upperPath = path?.[0];
const finalPath = path?.slice(1).join('.');
return generateInternalReflections({
bodyshop,
upperPath,
finalPath,
t
});
};
const reflections = reflector ? generateReflections(reflector) : [];
const fieldPath = [[field.name, "value"]];
// We have reflections so we will use a select box
if (reflections.length > 0) {
// We have reflections and the operator type is array, so we will use a select box with multiple options
if (operatorType === "array") {
return (
<Select
disabled={!operator}
mode="multiple"
options={reflections}
getPopupContainer={trigger => trigger.parentNode}
onChange={(value) => {
form.setFieldValue(fieldPath, value);
}}
/>
);
}
return (
<Select
options={reflections}
getPopupContainer={trigger => trigger.parentNode}
onChange={(value) => {
form.setFieldValue(fieldPath, value);
}}
/>
);
}
// We have a type of number, so we will use a number input
if (type === "number") {
return (
<InputNumber
disabled={!operator}
onChange={(value) => form.setFieldValue(fieldPath, value)}/>
);
}
// We have a type of date, so we will use a date picker
if (type === "date") {
return (
<FormDatePicker
disabled={!operator}
onChange={(date) => form.setFieldValue(fieldPath, date)}
/>
);
}
// we have a type of boolean, so we will use a select box with a true or false option.
if (type === "boolean" || type === "bool") {
return (
<Select
disabled={!operator}
getPopupContainer={trigger => trigger.parentNode}
options={[
{
label: t('reportcenter.labels.advanced_filters_true'),
value: true
},
{
label: t('reportcenter.labels.advanced_filters_false'),
value: false
}
]}
onChange={(value) => form.setFieldValue(fieldPath, value)}
/>
);
}
return (
<Input
disabled={!operator}
onChange={(e) => form.setFieldValue(fieldPath, e.target.value)}
/>
);
})()
}
</Form.Item>
}
}
</Form.Item>
</Col>
<Col span={2}>
<DeleteFilled
style={{margin: "1rem", paddingTop: '23px'}}
onClick={() => {
remove(field.name);
}}
/>
</Col>
</Row>
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{width: "100%"}}
>
{t("general.actions.add")}
</Button>
form.setFieldValue(["filters", field.name, "value"], undefined);
}}
/>
</Form.Item>
</div>
);
}}
</Form.List>
</Card>
);
);
}}
</Form.Item>
</Col>
<Col span={6}>
<Form.Item
dependencies={[
["filters", field.name, "field"],
["filters", field.name, "operator"]
]}
>
{() => {
// Because it looks cleaner than inlining.
const name = form.getFieldValue(["filters", field.name, "field"]);
const type = filters.find((f) => f.name === name)?.type;
const reflector = filters.find((f) => f.name === name)?.reflector;
const operator = form.getFieldValue(["filters", field.name, "operator"]);
const operatorType = operator
? getWhereOperatorsByType(type).find((o) => o.value === operator)?.type
: null;
return (
<Form.Item
key={`${index}value`}
label={t("reportcenter.labels.advanced_filters_filter_value")}
name={[field.name, "value"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
{(() => {
const generateReflections = (reflector) => {
if (!reflector) return [];
const { name } = reflector;
const path = name?.split(".");
const upperPath = path?.[0];
const finalPath = path?.slice(1).join(".");
return generateInternalReflections({
bodyshop,
upperPath,
finalPath,
t
});
};
const reflections = reflector ? generateReflections(reflector) : [];
const fieldPath = [[field.name, "value"]];
// We have reflections so we will use a select box
if (reflections.length > 0) {
// We have reflections and the operator type is array, so we will use a select box with multiple options
if (operatorType === "array") {
return (
<Select
disabled={!operator}
mode="multiple"
options={reflections}
getPopupContainer={(trigger) => trigger.parentNode}
onChange={(value) => {
form.setFieldValue(fieldPath, value);
}}
/>
);
}
return (
<Select
options={reflections}
getPopupContainer={(trigger) => trigger.parentNode}
onChange={(value) => {
form.setFieldValue(fieldPath, value);
}}
/>
);
}
// We have a type of number, so we will use a number input
if (type === "number") {
return (
<InputNumber
disabled={!operator}
onChange={(value) => form.setFieldValue(fieldPath, value)}
/>
);
}
// We have a type of date, so we will use a date picker
if (type === "date") {
return (
<FormDatePicker
disabled={!operator}
onChange={(date) => form.setFieldValue(fieldPath, date)}
/>
);
}
// we have a type of boolean, so we will use a select box with a true or false option.
if (type === "boolean" || type === "bool") {
return (
<Select
disabled={!operator}
getPopupContainer={(trigger) => trigger.parentNode}
options={[
{
label: t("reportcenter.labels.advanced_filters_true"),
value: true
},
{
label: t("reportcenter.labels.advanced_filters_false"),
value: false
}
]}
onChange={(value) => form.setFieldValue(fieldPath, value)}
/>
);
}
return (
<Input
disabled={!operator}
onChange={(e) => form.setFieldValue(fieldPath, e.target.value)}
/>
);
})()}
</Form.Item>
);
}}
</Form.Item>
</Col>
<Col span={2}>
<DeleteFilled
style={{ margin: "1rem", paddingTop: "23px" }}
onClick={() => {
remove(field.name);
}}
/>
</Col>
</Row>
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</Card>
);
}
/**
@@ -266,88 +272,90 @@ function FiltersSection({filters, form, bodyshop}) {
* @returns {JSX.Element}
* @constructor
*/
function SortersSection({sorters}) {
const {t} = useTranslation();
return (
<Card type='inner' title={t('reportcenter.labels.advanced_filters_sorters')} style={{marginTop: '10px'}}>
<Form.List name={["sorters"]}>
{(fields, {add, remove}) => {
return (
<div>
Sorters
{fields.map((field, index) => (
<Form.Item key={field.key}>
<Row gutter={[16, 16]}>
<Col span={11}>
<Form.Item
key={`${index}field`}
label={t('reportcenter.labels.advanced_filters_sorter_field')}
name={[field.name, "field"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select
options={
sorters.map((f) => ({
value: f.name,
label: f?.translation ? (t(f.translation) === f.translation ? f.label : t(f.translation)) : f.label,
}))
}
getPopupContainer={trigger => trigger.parentNode}
/>
</Form.Item>
</Col>
<Col span={11}>
<Form.Item
key={`${index}direction`}
label={t('reportcenter.labels.advanced_filters_sorter_direction')}
name={[field.name, "direction"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select
options={getOrderOperatorsByType()}
getPopupContainer={trigger => trigger.parentNode}
/>
</Form.Item>
</Col>
function SortersSection({ sorters }) {
const { t } = useTranslation();
return (
<Card type="inner" title={t("reportcenter.labels.advanced_filters_sorters")} style={{ marginTop: "10px" }}>
<Form.List name={["sorters"]}>
{(fields, { add, remove }) => {
return (
<div>
Sorters
{fields.map((field, index) => (
<Form.Item key={field.key}>
<Row gutter={[16, 16]}>
<Col span={11}>
<Form.Item
key={`${index}field`}
label={t("reportcenter.labels.advanced_filters_sorter_field")}
name={[field.name, "field"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Select
options={sorters.map((f) => ({
value: f.name,
label: f?.translation
? t(f.translation) === f.translation
? f.label
: t(f.translation)
: f.label
}))}
getPopupContainer={(trigger) => trigger.parentNode}
/>
</Form.Item>
</Col>
<Col span={11}>
<Form.Item
key={`${index}direction`}
label={t("reportcenter.labels.advanced_filters_sorter_direction")}
name={[field.name, "direction"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Select
options={getOrderOperatorsByType()}
getPopupContainer={(trigger) => trigger.parentNode}
/>
</Form.Item>
</Col>
<Col span={2}>
<DeleteFilled
style={{margin: "1rem", paddingTop: '23px'}}
onClick={() => {
remove(field.name);
}}
/>
</Col>
</Row>
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{width: "100%"}}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</Card>
);
<Col span={2}>
<DeleteFilled
style={{ margin: "1rem", paddingTop: "23px" }}
onClick={() => {
remove(field.name);
}}
/>
</Col>
</Row>
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</Card>
);
}
/**
@@ -358,75 +366,73 @@ function SortersSection({sorters}) {
* @returns {JSX.Element|null}
* @constructor
*/
function RenderFilters({templateId, form, bodyshop}) {
const [state, setState] = useState(null);
const [visible, setVisible] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const {t} = useTranslation();
function RenderFilters({ templateId, form, bodyshop }) {
const [state, setState] = useState(null);
const [visible, setVisible] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const { t } = useTranslation();
const fetch = useCallback(async () => {
// Reset all the filters and Sorters.
form.resetFields(['filters']);
form.resetFields(['sorters']);
form.resetFields(['defaultSorters']);
const fetch = useCallback(async () => {
// Reset all the filters and Sorters.
form.resetFields(["filters"]);
form.resetFields(["sorters"]);
form.resetFields(["defaultSorters"]);
setIsLoading(true);
setIsLoading(true);
const data = await fetchFilterData({name: templateId});
const data = await fetchFilterData({ name: templateId });
// We have Success
if (data?.success) {
if (data?.data?.sorters && data?.data?.sorters.length > 0) {
const defaultSorters = data?.data?.sorters.filter((sorter) => sorter.hasOwnProperty('default')).map((sorter) => {
return {
field: sorter.name,
direction: sorter.default.direction
};
}).sort((a, b) => a.default.order - b.default.order);
// We have Success
if (data?.success) {
if (data?.data?.sorters && data?.data?.sorters.length > 0) {
const defaultSorters = data?.data?.sorters
.filter((sorter) => sorter.hasOwnProperty("default"))
.map((sorter) => {
return {
field: sorter.name,
direction: sorter.default.direction
};
})
.sort((a, b) => a.default.order - b.default.order);
form.setFieldValue('defaultSorters', JSON.stringify(defaultSorters));
}
// Set the state
setState(data.data);
}
// Something went wrong fetching filter data
else {
setState(null);
}
setIsLoading(false);
}, [templateId, form]);
form.setFieldValue("defaultSorters", JSON.stringify(defaultSorters));
}
// Set the state
setState(data.data);
}
// Something went wrong fetching filter data
else {
setState(null);
}
setIsLoading(false);
}, [templateId, form]);
useEffect(() => {
if (templateId) {
fetch();
useEffect(() => {
if (templateId) {
fetch();
}
}, [templateId, fetch]);
}
}, [templateId, fetch]);
const filters = useMemo(() => state?.filters || [], [state]);
const sorters = useMemo(() => state?.sorters || [], [state]);
const filters = useMemo(() => state?.filters || [], [state]);
const sorters = useMemo(() => state?.sorters || [], [state]);
if (!templateId) return null;
if (isLoading) return <LoadingSkeleton />;
if (!state) return null;
if (!templateId) return null;
if (isLoading) return <LoadingSkeleton/>;
if (!state) return null;
return (
<div style={{marginTop: '10px'}}>
<Checkbox
checked={visible}
onChange={(e) => setVisible(e.target.checked)}
children={t('reportcenter.labels.advanced_filters')}
/>
{visible && (
<div>
{filters.length > 0 && (
<FiltersSection filters={filters} form={form} bodyshop={bodyshop}/>
)}
{sorters.length > 0 && (
<SortersSection sorters={sorters} form={form}/>
)}
</div>
)}
return (
<div style={{ marginTop: "10px" }}>
<Checkbox
checked={visible}
onChange={(e) => setVisible(e.target.checked)}
children={t("reportcenter.labels.advanced_filters")}
/>
{visible && (
<div>
{filters.length > 0 && <FiltersSection filters={filters} form={form} bodyshop={bodyshop} />}
{sorters.length > 0 && <SortersSection sorters={sorters} form={form} />}
</div>
);
}
)}
</div>
);
}

View File

@@ -1,4 +1,4 @@
import {uniqBy} from "lodash";
import { uniqBy } from "lodash";
/**
* Get value from path
@@ -6,7 +6,7 @@ import {uniqBy} from "lodash";
* @param path
* @returns {*}
*/
const getValueFromPath = (obj, path) => path.split('.').reduce((prev, curr) => prev?.[curr], obj);
const getValueFromPath = (obj, path) => path.split(".").reduce((prev, curr) => prev?.[curr], obj);
/**
* Generate options from array
@@ -15,12 +15,15 @@ const getValueFromPath = (obj, path) => path.split('.').reduce((prev, curr) => p
* @returns {unknown[]}
*/
const generateOptionsFromArray = (bodyshop, path) => {
const options = getValueFromPath(bodyshop, path);
return uniqBy(options.map((value) => ({
label: value,
value: value,
})), 'value');
}
const options = getValueFromPath(bodyshop, path);
return uniqBy(
options.map((value) => ({
label: value,
value: value
})),
"value"
);
};
/**
* Valid internal reflections
@@ -28,12 +31,12 @@ const generateOptionsFromArray = (bodyshop, path) => {
* @type {{special: string[], bodyshop: [{name: string, type: string}]}}
*/
const VALID_INTERNAL_REFLECTIONS = {
bodyshop: [
{
name: 'md_ro_statuses.statuses',
type: 'kv-to-v'
}
],
bodyshop: [
{
name: "md_ro_statuses.statuses",
type: "kv-to-v"
}
]
};
/**
@@ -45,12 +48,15 @@ const VALID_INTERNAL_REFLECTIONS = {
* @returns {{label: *, value: *}[]}
*/
const generateOptionsFromObject = (bodyshop, path, labelPath, valuePath) => {
const options = getValueFromPath(bodyshop, path);
return uniqBy(Object.values(options).map((value) => ({
label: value[labelPath],
value: value[valuePath],
})), 'value');
}
const options = getValueFromPath(bodyshop, path);
return uniqBy(
Object.values(options).map((value) => ({
label: value[labelPath],
value: value[valuePath]
})),
"value"
);
};
/**
* Generate special reflections
@@ -60,66 +66,69 @@ const generateOptionsFromObject = (bodyshop, path, labelPath, valuePath) => {
* @returns {{label: *, value: *}[]|{label: *, value: *}[]|{label: string, value: *}[]|*[]}
*/
const generateSpecialReflections = (bodyshop, finalPath, t) => {
switch (finalPath) {
case 'payment_payers':
return [
{
label: t("payments.labels.customer"),
value: t("payments.labels.customer"),
},
{
label: t("payments.labels.insurance"),
value: t("payments.labels.insurance"),
},
// This is a weird one supposedly only used by one shop and could potentially be
// placed behind a SplitSDK
{
label: t("payments.labels.external"),
value: t("payments.labels.external"),
}
];
case 'payment_types':
return generateOptionsFromArray(bodyshop, 'md_payment_types');
case 'alt_transports':
return generateOptionsFromArray(bodyshop, 'appt_alt_transport');
case 'lost_sale_reasons':
return generateOptionsFromArray(bodyshop, 'md_lost_sale_reasons');
// Special case because Referral Sources is an Array, not an Object.
case 'referral_source':
return generateOptionsFromArray(bodyshop, 'md_referral_sources');
case 'class':
return generateOptionsFromArray(bodyshop, 'md_classes');
case 'cost_centers':
return generateOptionsFromObject(bodyshop, 'md_responsibility_centers.costs', 'name', 'name');
// Special case because Categories is an Array, not an Object.
case 'categories':
return generateOptionsFromArray(bodyshop, 'md_categories');
case 'insurance_companies':
return generateOptionsFromObject(bodyshop, 'md_ins_cos', 'name', 'name');
case 'employee_teams':
return generateOptionsFromObject(bodyshop, 'employee_teams', 'name', 'id');
// Special case because Employees uses a concatenation of first_name and last_name
case 'employees':
const employeesOptions = getValueFromPath(bodyshop, 'employees');
return uniqBy(Object.values(employeesOptions).map((value) => ({
label: `${value.first_name} ${value.last_name}`,
value: value.id,
})), 'value');
case 'last_names':
return generateOptionsFromObject(bodyshop, 'employees', 'last_name', 'last_name');
case 'first_names':
return generateOptionsFromObject(bodyshop, 'employees', 'first_name', 'first_name');
case 'job_statuses':
const statusOptions = getValueFromPath(bodyshop, 'md_ro_statuses.statuses');
return Object.values(statusOptions).map((value) => ({
label: value,
value
}));
default:
console.error('Invalid Special reflection provided by Report Filters');
return [];
}
}
switch (finalPath) {
case "payment_payers":
return [
{
label: t("payments.labels.customer"),
value: t("payments.labels.customer")
},
{
label: t("payments.labels.insurance"),
value: t("payments.labels.insurance")
},
// This is a weird one supposedly only used by one shop and could potentially be
// placed behind a SplitSDK
{
label: t("payments.labels.external"),
value: t("payments.labels.external")
}
];
case "payment_types":
return generateOptionsFromArray(bodyshop, "md_payment_types");
case "alt_transports":
return generateOptionsFromArray(bodyshop, "appt_alt_transport");
case "lost_sale_reasons":
return generateOptionsFromArray(bodyshop, "md_lost_sale_reasons");
// Special case because Referral Sources is an Array, not an Object.
case "referral_source":
return generateOptionsFromArray(bodyshop, "md_referral_sources");
case "class":
return generateOptionsFromArray(bodyshop, "md_classes");
case "cost_centers":
return generateOptionsFromObject(bodyshop, "md_responsibility_centers.costs", "name", "name");
// Special case because Categories is an Array, not an Object.
case "categories":
return generateOptionsFromArray(bodyshop, "md_categories");
case "insurance_companies":
return generateOptionsFromObject(bodyshop, "md_ins_cos", "name", "name");
case "employee_teams":
return generateOptionsFromObject(bodyshop, "employee_teams", "name", "id");
// Special case because Employees uses a concatenation of first_name and last_name
case "employees":
const employeesOptions = getValueFromPath(bodyshop, "employees");
return uniqBy(
Object.values(employeesOptions).map((value) => ({
label: `${value.first_name} ${value.last_name}`,
value: value.id
})),
"value"
);
case "last_names":
return generateOptionsFromObject(bodyshop, "employees", "last_name", "last_name");
case "first_names":
return generateOptionsFromObject(bodyshop, "employees", "first_name", "first_name");
case "job_statuses":
const statusOptions = getValueFromPath(bodyshop, "md_ro_statuses.statuses");
return Object.values(statusOptions).map((value) => ({
label: value,
value
}));
default:
console.error("Invalid Special reflection provided by Report Filters");
return [];
}
};
/**
* Generate bodyshop reflections
@@ -128,16 +137,16 @@ const generateSpecialReflections = (bodyshop, finalPath, t) => {
* @returns {{label: *, value: *}[]|*[]}
*/
const generateBodyshopReflections = (bodyshop, finalPath) => {
const options = getValueFromPath(bodyshop, finalPath);
const reflectionRenderer = VALID_INTERNAL_REFLECTIONS.bodyshop.find(reflection => reflection.name === finalPath);
if (reflectionRenderer?.type === 'kv-to-v') {
return Object.values(options).map((value) => ({
label: value,
value
}));
}
return [];
}
const options = getValueFromPath(bodyshop, finalPath);
const reflectionRenderer = VALID_INTERNAL_REFLECTIONS.bodyshop.find((reflection) => reflection.name === finalPath);
if (reflectionRenderer?.type === "kv-to-v") {
return Object.values(options).map((value) => ({
label: value,
value
}));
}
return [];
};
/**
* Generate internal reflections based on the path and bodyshop
@@ -147,15 +156,15 @@ const generateBodyshopReflections = (bodyshop, finalPath) => {
* @param t - i18n
* @returns {{label: *, value: *}[]|[]|{label: *, value: *}[]|{label: string, value: *}[]|{label: *, value: *}[]|*[]}
*/
const generateInternalReflections = ({bodyshop, upperPath, finalPath, t}) => {
switch (upperPath) {
case 'special':
return generateSpecialReflections(bodyshop, finalPath, t);
case 'bodyshop':
return generateBodyshopReflections(bodyshop, finalPath);
default:
return [];
}
const generateInternalReflections = ({ bodyshop, upperPath, finalPath, t }) => {
switch (upperPath) {
case "special":
return generateSpecialReflections(bodyshop, finalPath, t);
case "bodyshop":
return generateBodyshopReflections(bodyshop, finalPath);
default:
return [];
}
};
export {generateInternalReflections}
export { generateInternalReflections };

View File

@@ -1,19 +1,19 @@
import {useLazyQuery} from "@apollo/client";
import {useSplitTreatments} from "@splitsoftware/splitio-react";
import {Button, Card, Col, DatePicker, Form, Input, Radio, Row, Typography,} from "antd";
import { useLazyQuery } from "@apollo/client";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import { Button, Card, Col, DatePicker, Form, Input, Radio, Row, Typography } from "antd";
import _ from "lodash";
import dayjs from "../../utils/day";
import React, {useState} from "react";
import {useTranslation} from "react-i18next";
import {connect} from "react-redux";
import {createStructuredSelector} from "reselect";
import {QUERY_ACTIVE_EMPLOYEES} from "../../graphql/employees.queries";
import {QUERY_ALL_VENDORS} from "../../graphql/vendors.queries";
import {selectReportCenter} from "../../redux/modals/modals.selectors";
import {selectBodyshop} from "../../redux/user/user.selectors";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { QUERY_ACTIVE_EMPLOYEES } from "../../graphql/employees.queries";
import { QUERY_ALL_VENDORS } from "../../graphql/vendors.queries";
import { selectReportCenter } from "../../redux/modals/modals.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import DatePickerRanges from "../../utils/DatePickerRanges";
import {GenerateDocument} from "../../utils/RenderTemplate";
import {TemplateList} from "../../utils/TemplateConstants";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component";
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
import "./report-center-modal.styles.scss";
@@ -21,331 +21,275 @@ import ReportCenterModalFiltersSortersComponent from "./report-center-modal-filt
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
const mapStateToProps = createStructuredSelector({
reportCenterModal: selectReportCenter,
bodyshop: selectBodyshop,
reportCenterModal: selectReportCenter,
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(ReportCenterModalComponent);
export default connect(mapStateToProps, mapDispatchToProps)(ReportCenterModalComponent);
export function ReportCenterModalComponent({reportCenterModal, bodyshop}) {
const [form] = Form.useForm();
const [search, setSearch] = useState("");
const {treatments: {Enhanced_Payroll}} = useSplitTreatments({
attributes: {},
names: ["Enhanced_Payroll"],
splitKey: bodyshop.imexshopid,
});
export function ReportCenterModalComponent({ reportCenterModal, bodyshop }) {
const [form] = Form.useForm();
const [search, setSearch] = useState("");
const {
treatments: { Enhanced_Payroll }
} = useSplitTreatments({
attributes: {},
names: ["Enhanced_Payroll"],
splitKey: bodyshop.imexshopid
});
const [loading, setLoading] = useState(false);
const {t} = useTranslation();
const Templates = TemplateList("report_center");
const ReportsList =
Enhanced_Payroll.treatment === "on"
? Object.keys(Templates)
.map((key) => {
return Templates[key];
})
.filter(
(temp) =>
temp.enhanced_payroll === undefined ||
temp.enhanced_payroll === true
)
: Object.keys(Templates)
.map((key) => {
return Templates[key];
})
.filter(
(temp) =>
temp.enhanced_payroll === undefined ||
temp.enhanced_payroll === false
);
const [loading, setLoading] = useState(false);
const { t } = useTranslation();
const Templates = TemplateList("report_center");
const ReportsList =
Enhanced_Payroll.treatment === "on"
? Object.keys(Templates)
.map((key) => {
return Templates[key];
})
.filter((temp) => temp.enhanced_payroll === undefined || temp.enhanced_payroll === true)
: Object.keys(Templates)
.map((key) => {
return Templates[key];
})
.filter((temp) => temp.enhanced_payroll === undefined || temp.enhanced_payroll === false);
const {open} = reportCenterModal;
const { open } = reportCenterModal;
const [callVendorQuery, {data: vendorData, called: vendorCalled}] =
useLazyQuery(QUERY_ALL_VENDORS, {
skip: !(
open &&
Templates[form.getFieldValue("key")] &&
Templates[form.getFieldValue("key")].idtype
),
});
const [callVendorQuery, { data: vendorData, called: vendorCalled }] = useLazyQuery(QUERY_ALL_VENDORS, {
skip: !(open && Templates[form.getFieldValue("key")] && Templates[form.getFieldValue("key")].idtype)
});
const [callEmployeeQuery, {data: employeeData, called: employeeCalled}] =
useLazyQuery(QUERY_ACTIVE_EMPLOYEES, {
skip: !(
open &&
Templates[form.getFieldValue("key")] &&
Templates[form.getFieldValue("key")].idtype
),
});
const [callEmployeeQuery, { data: employeeData, called: employeeCalled }] = useLazyQuery(QUERY_ACTIVE_EMPLOYEES, {
skip: !(open && Templates[form.getFieldValue("key")] && Templates[form.getFieldValue("key")].idtype)
});
const handleFinish = async (values) => {
setLoading(true);
const start = values.dates ? values.dates[0] : null;
const end = values.dates ? values.dates[1] : null;
const {id} = values;
const handleFinish = async (values) => {
setLoading(true);
const start = values.dates ? values.dates[0] : null;
const end = values.dates ? values.dates[1] : null;
const { id } = values;
const templateConfig =
{
name: values.key,
variables: {
...(start
? {start: dayjs(start).startOf("day").format("YYYY-MM-DD")}
: {}),
...(end
? {end: dayjs(end).endOf("day").format("YYYY-MM-DD")}
: {}),
...(start ? {starttz: dayjs(start).startOf("day")} : {}),
...(end ? {endtz: dayjs(end).endOf("day")} : {}),
const templateConfig = {
name: values.key,
variables: {
...(start ? { start: dayjs(start).startOf("day").format("YYYY-MM-DD") } : {}),
...(end ? { end: dayjs(end).endOf("day").format("YYYY-MM-DD") } : {}),
...(start ? { starttz: dayjs(start).startOf("day") } : {}),
...(end ? { endtz: dayjs(end).endOf("day") } : {}),
...(id ? {id: id} : {}),
},
filters: values.filters,
sorters: values.sorters,
};
if (_.isString(values.defaultSorters) && !_.isEmpty(values.defaultSorters)) {
templateConfig.defaultSorters = JSON.parse(values.defaultSorters);
}
await GenerateDocument(
templateConfig,
{
to: values.to,
subject: Templates[values.key]?.subject,
},
values.sendbyexcel === "excel"
? "x"
: values.sendby === "email"
? "e"
: "p",
id
);
setLoading(false);
...(id ? { id: id } : {})
},
filters: values.filters,
sorters: values.sorters
};
const FilteredReportsList =
search !== ""
? ReportsList.filter((r) =>
r.title.toLowerCase().includes(search.toLowerCase())
)
: ReportsList;
if (_.isString(values.defaultSorters) && !_.isEmpty(values.defaultSorters)) {
templateConfig.defaultSorters = JSON.parse(values.defaultSorters);
}
//Group it, create cards, and then filter out.
await GenerateDocument(
templateConfig,
{
to: values.to,
subject: Templates[values.key]?.subject
},
values.sendbyexcel === "excel" ? "x" : values.sendby === "email" ? "e" : "p",
id
);
setLoading(false);
};
const grouped = _.groupBy(FilteredReportsList, "group");
const FilteredReportsList =
search !== "" ? ReportsList.filter((r) => r.title.toLowerCase().includes(search.toLowerCase())) : ReportsList;
const groupExcludeKeyFilter = [
...(!HasFeatureAccess({ featureName: 'bills', bodyshop }) ? ['purchases'] : []),
...(!HasFeatureAccess({ featureName: 'timetickets', bodyshop }) ? ['payroll'] : []),
];
//Group it, create cards, and then filter out.
return (
<div>
<Form
onFinish={handleFinish}
autoComplete={"off"}
layout="vertical"
form={form}
>
<Input.Search
onChange={(e) => setSearch(e.target.value)}
value={search}
/>
<Form.Item name="defaultSorters" hidden/>
<Form.Item
name="key"
label={t("reportcenter.labels.key")}
// className="radio-group-columns"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Radio.Group>
{/* {Object.keys(Templates).map((key) => (
const grouped = _.groupBy(FilteredReportsList, "group");
const groupExcludeKeyFilter = [
...(!HasFeatureAccess({ featureName: "bills", bodyshop }) ? ["purchases"] : []),
...(!HasFeatureAccess({ featureName: "timetickets", bodyshop }) ? ["payroll"] : [])
];
return (
<div>
<Form onFinish={handleFinish} autoComplete={"off"} layout="vertical" form={form}>
<Input.Search onChange={(e) => setSearch(e.target.value)} value={search} />
<Form.Item name="defaultSorters" hidden />
<Form.Item
name="key"
label={t("reportcenter.labels.key")}
// className="radio-group-columns"
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Radio.Group>
{/* {Object.keys(Templates).map((key) => (
<Radio key={key} value={key}>
{Templates[key].title}
</Radio>
))} */}
<Row gutter={[16, 16]}>
{Object.keys(grouped).filter(key => !groupExcludeKeyFilter.includes(key)).map((key) => (
<Col md={8} sm={12} key={key}>
<Card.Grid
style={{
width: "100%",
height: "100%",
maxHeight: "33vh",
overflowY: "scroll",
}}
>
<Typography.Title level={4}>
{t(`reportcenter.labels.groups.${key}`)}
</Typography.Title>
<ul style={{listStyleType: 'none', columns: "2 auto"}}>
{grouped[key].map((item) => (
<li key={item.key}>
<Radio key={item.key} value={item.key}>
{item.title}
</Radio>
</li>
))}
</ul>
</Card.Grid>
</Col>
))}
</Row>
</Radio.Group>
</Form.Item>
<Form.Item style={{margin: 0, padding: 0}} dependencies={["key"]}>
{() => {
const key = form.getFieldValue("key");
if (!key) return null;
//Kind of Id
const rangeFilter = Templates[key] && Templates[key].rangeFilter;
if (!rangeFilter) return null;
return (
<div>
{t("reportcenter.labels.filterson", {
object: rangeFilter.object,
field: rangeFilter.field,
})}
</div>
);
}}
</Form.Item>
<ReportCenterModalFiltersSortersComponent form={form} bodyshop={bodyshop}/>
<Form.Item style={{margin: 0, padding: 0}} dependencies={["key"]}>
{() => {
const key = form.getFieldValue("key");
const currentId = form.getFieldValue("id");
if (!key) return null;
//Kind of Id
const idtype = Templates[key] && Templates[key].idtype;
if (!idtype && currentId) {
form.setFieldsValue({id: null});
return null;
}
if (!vendorCalled && idtype === "vendor") callVendorQuery();
if (!employeeCalled && idtype === "employee") callEmployeeQuery();
if (idtype === "vendor")
return (
<Form.Item
name="id"
label={t("reportcenter.labels.vendor")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<VendorSearchSelect
options={vendorData ? vendorData.vendors : []}
/>
</Form.Item>
);
if (idtype === "employee")
return (
<Form.Item
name="id"
label={t("reportcenter.labels.employee")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<EmployeeSearchSelect
options={employeeData ? employeeData.employees : []}
/>
</Form.Item>
);
else return null;
}}
</Form.Item>
<Form.Item style={{margin: 0, padding: 0}} dependencies={["key"]}>
{() => {
const key = form.getFieldValue("key");
const datedisable = Templates[key] && Templates[key].datedisable;
if (datedisable !== true) {
return (
<Form.Item
name="dates"
label={t("reportcenter.labels.dates")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<DatePicker.RangePicker
format="MM/DD/YYYY"
presets={DatePickerRanges}
/>
</Form.Item>
);
} else return null;
}}
</Form.Item>
<Form.Item style={{margin: 0, padding: 0}} dependencies={["key"]}>
{() => {
const key = form.getFieldValue("key");
//Kind of Id
const reporttype = Templates[key] && Templates[key].reporttype;
if (reporttype === "excel")
return (
<Form.Item
label={t("general.labels.sendby")}
name="sendbyexcel"
initialValue="excel"
>
<Radio.Group>
<Radio value="excel">{t("general.labels.excel")}</Radio>
</Radio.Group>
</Form.Item>
);
if (reporttype !== "excel")
return (
<Form.Item
label={t("general.labels.sendby")}
name="sendby"
initialValue="print"
>
<Radio.Group>
<Radio value="email">{t("general.labels.email")}</Radio>
<Radio value="print">{t("general.labels.print")}</Radio>
</Radio.Group>
</Form.Item>
);
}}
</Form.Item>
<div
style={{
display: "flex",
justifyContent: "center",
marginTop: "1rem",
}}
<Row gutter={[16, 16]}>
{Object.keys(grouped)
.filter((key) => !groupExcludeKeyFilter.includes(key))
.map((key) => (
<Col md={8} sm={12} key={key}>
<Card.Grid
style={{
width: "100%",
height: "100%",
maxHeight: "33vh",
overflowY: "scroll"
}}
>
<Typography.Title level={4}>{t(`reportcenter.labels.groups.${key}`)}</Typography.Title>
<ul style={{ listStyleType: "none", columns: "2 auto" }}>
{grouped[key].map((item) => (
<li key={item.key}>
<Radio key={item.key} value={item.key}>
{item.title}
</Radio>
</li>
))}
</ul>
</Card.Grid>
</Col>
))}
</Row>
</Radio.Group>
</Form.Item>
<Form.Item style={{ margin: 0, padding: 0 }} dependencies={["key"]}>
{() => {
const key = form.getFieldValue("key");
if (!key) return null;
//Kind of Id
const rangeFilter = Templates[key] && Templates[key].rangeFilter;
if (!rangeFilter) return null;
return (
<div>
{t("reportcenter.labels.filterson", {
object: rangeFilter.object,
field: rangeFilter.field
})}
</div>
);
}}
</Form.Item>
<ReportCenterModalFiltersSortersComponent form={form} bodyshop={bodyshop} />
<Form.Item style={{ margin: 0, padding: 0 }} dependencies={["key"]}>
{() => {
const key = form.getFieldValue("key");
const currentId = form.getFieldValue("id");
if (!key) return null;
//Kind of Id
const idtype = Templates[key] && Templates[key].idtype;
if (!idtype && currentId) {
form.setFieldsValue({ id: null });
return null;
}
if (!vendorCalled && idtype === "vendor") callVendorQuery();
if (!employeeCalled && idtype === "employee") callEmployeeQuery();
if (idtype === "vendor")
return (
<Form.Item
name="id"
label={t("reportcenter.labels.vendor")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Button onClick={() => form.submit()} style={{}} loading={loading}>
{t("reportcenter.actions.generate")}
</Button>
</div>
</Form>
</div>
);
}
<VendorSearchSelect options={vendorData ? vendorData.vendors : []} />
</Form.Item>
);
if (idtype === "employee")
return (
<Form.Item
name="id"
label={t("reportcenter.labels.employee")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<EmployeeSearchSelect options={employeeData ? employeeData.employees : []} />
</Form.Item>
);
else return null;
}}
</Form.Item>
<Form.Item style={{ margin: 0, padding: 0 }} dependencies={["key"]}>
{() => {
const key = form.getFieldValue("key");
const datedisable = Templates[key] && Templates[key].datedisable;
if (datedisable !== true) {
return (
<Form.Item
name="dates"
label={t("reportcenter.labels.dates")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<DatePicker.RangePicker format="MM/DD/YYYY" presets={DatePickerRanges} />
</Form.Item>
);
} else return null;
}}
</Form.Item>
<Form.Item style={{ margin: 0, padding: 0 }} dependencies={["key"]}>
{() => {
const key = form.getFieldValue("key");
//Kind of Id
const reporttype = Templates[key] && Templates[key].reporttype;
if (reporttype === "excel")
return (
<Form.Item label={t("general.labels.sendby")} name="sendbyexcel" initialValue="excel">
<Radio.Group>
<Radio value="excel">{t("general.labels.excel")}</Radio>
</Radio.Group>
</Form.Item>
);
if (reporttype !== "excel")
return (
<Form.Item label={t("general.labels.sendby")} name="sendby" initialValue="print">
<Radio.Group>
<Radio value="email">{t("general.labels.email")}</Radio>
<Radio value="print">{t("general.labels.print")}</Radio>
</Radio.Group>
</Form.Item>
);
}}
</Form.Item>
<div
style={{
display: "flex",
justifyContent: "center",
marginTop: "1rem"
}}
>
<Button onClick={() => form.submit()} style={{}} loading={loading}>
{t("reportcenter.actions.generate")}
</Button>
</div>
</Form>
</div>
);
}

View File

@@ -1,47 +1,41 @@
import {Modal} from "antd";
import { Modal } from "antd";
import React from "react";
import {useTranslation} from "react-i18next";
import {connect} from "react-redux";
import {createStructuredSelector} from "reselect";
import {toggleModalVisible} from "../../redux/modals/modals.actions";
import {selectReportCenter} from "../../redux/modals/modals.selectors";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectReportCenter } from "../../redux/modals/modals.selectors";
import RbacWrapperComponent from "../rbac-wrapper/rbac-wrapper.component";
import ReportCenterModalComponent from "./report-center-modal.component";
const mapStateToProps = createStructuredSelector({
reportCenterModal: selectReportCenter,
reportCenterModal: selectReportCenter
});
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("reportCenter")),
toggleModalVisible: () => dispatch(toggleModalVisible("reportCenter"))
});
export function ReportCenterModalContainer({
reportCenterModal,
toggleModalVisible,
}) {
const {t} = useTranslation();
export function ReportCenterModalContainer({ reportCenterModal, toggleModalVisible }) {
const { t } = useTranslation();
const {open} = reportCenterModal;
const { open } = reportCenterModal;
return (
<Modal
open={open}
title={t("printcenter.labels.reportcentermodal")}
onOk={() => toggleModalVisible()}
onCancel={() => toggleModalVisible()}
cancelButtonProps={{style: {display: "none"}}}
destroyOnClose
width="80%"
>
<RbacWrapperComponent action="shop:reportcenter">
<ReportCenterModalComponent/>
</RbacWrapperComponent>
</Modal>
);
return (
<Modal
open={open}
title={t("printcenter.labels.reportcentermodal")}
onOk={() => toggleModalVisible()}
onCancel={() => toggleModalVisible()}
cancelButtonProps={{ style: { display: "none" } }}
destroyOnClose
width="80%"
>
<RbacWrapperComponent action="shop:reportcenter">
<ReportCenterModalComponent />
</RbacWrapperComponent>
</Modal>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ReportCenterModalContainer);
export default connect(mapStateToProps, mapDispatchToProps)(ReportCenterModalContainer);