- Progress Commit

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-02-28 17:17:17 -05:00
parent 9daf992582
commit 32bba8060a
2 changed files with 276 additions and 175 deletions

View File

@@ -6,6 +6,7 @@ import {useTranslation} from "react-i18next";
import {getOrderOperatorsByType, getWhereOperatorsByType} from "../../utils/graphQLmodifier"; import {getOrderOperatorsByType, getWhereOperatorsByType} from "../../utils/graphQLmodifier";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component"; import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import {generateInternalReflections} from "./report-center-modal-utils"; import {generateInternalReflections} from "./report-center-modal-utils";
import {FormDatePicker} from "../form-date-picker/form-date-picker.component.jsx";
export default function ReportCenterModalFiltersSortersComponent({form, bodyshop}) { export default function ReportCenterModalFiltersSortersComponent({form, bodyshop}) {
@@ -72,7 +73,8 @@ function FiltersSection({filters, form, bodyshop}) {
</Col> </Col>
<Col span={6}> <Col span={6}>
<Form.Item <Form.Item
dependencies={[['filters', field.name, "field"]]}> dependencies={[['filters', field.name, "field"]]}
>
{ {
() => { () => {
const name = form.getFieldValue(['filters', field.name, "field"]); const name = form.getFieldValue(['filters', field.name, "field"]);
@@ -82,7 +84,6 @@ function FiltersSection({filters, form, bodyshop}) {
key={`${index}operator`} key={`${index}operator`}
label={t('reportcenter.labels.advanced_filters_filter_operator')} label={t('reportcenter.labels.advanced_filters_filter_operator')}
name={[field.name, "operator"]} name={[field.name, "operator"]}
dependencies={[]}
rules={[ rules={[
{ {
required: true, required: true,
@@ -92,7 +93,13 @@ function FiltersSection({filters, form, bodyshop}) {
> >
<Select <Select
getPopupContainer={trigger => trigger.parentNode} getPopupContainer={trigger => trigger.parentNode}
options={getWhereOperatorsByType(type)}/> options={getWhereOperatorsByType(type)}
onChange={() => {
// Clear related Fields
form.setFieldValue(['filters', field.name, 'value'], null);
}}
/>
</Form.Item> </Form.Item>
} }
} }
@@ -100,12 +107,19 @@ function FiltersSection({filters, form, bodyshop}) {
</Col> </Col>
<Col span={6}> <Col span={6}>
<Form.Item <Form.Item
dependencies={[['filters', field.name, "field"]]}> dependencies={[
['filters', field.name, "field"],
['filters', field.name, "operator"]
]}
>
{ {
() => { () => {
// Because it looks cleaner than inlining.
const name = form.getFieldValue(['filters', field.name, "field"]); const name = form.getFieldValue(['filters', field.name, "field"]);
const type = filters.find(f => f.name === name)?.type; const type = filters.find(f => f.name === name)?.type;
const reflector = filters.find(f => f.name === name)?.reflector; const reflector = filters.find(f => f.name === name)?.reflector;
const operator = form.getFieldValue(['filters', field.name, "operator"]);
const operatorType = getWhereOperatorsByType(type).find((o) => o.value === operator)?.type;
return <Form.Item return <Form.Item
key={`${index}value`} key={`${index}value`}
@@ -138,7 +152,23 @@ function FiltersSection({filters, form, bodyshop}) {
const reflections = reflector ? generateReflections(reflector) : []; const reflections = reflector ? generateReflections(reflector) : [];
const fieldPath = [[field.name, "value"]]; const fieldPath = [[field.name, "value"]];
// We have reflections so we will use a select box
if (reflections.length > 0) { if (reflections.length > 0) {
// We have reflections and the operator type is array, so we will use a select box with multiple options
console.log(`operatorType: ${operatorType}`)
console.log(`operator: ${operator}`)
if (operatorType === "array") {
return (
<Select
mode="multiple"
options={reflections}
getPopupContainer={trigger => trigger.parentNode}
onChange={(value) => {
form.setFieldValue(fieldPath, value);
}}
/>
);
}
return ( return (
<Select <Select
options={reflections} options={reflections}
@@ -150,6 +180,7 @@ function FiltersSection({filters, form, bodyshop}) {
); );
} }
// We have a type of number, so we will use a number input
if (type === "number") { if (type === "number") {
return ( return (
<InputNumber <InputNumber
@@ -157,6 +188,34 @@ function FiltersSection({filters, form, bodyshop}) {
); );
} }
// We have a type of date, so we will use a date picker
if (type === "date") {
return (
<FormDatePicker
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
getPopupContainer={trigger => trigger.parentNode}
options={[
{
label: "True",
value: true
},
{
label: "False",
value: false
}
]}
onChange={(value) => form.setFieldValue(fieldPath, value)}
/>
);
}
return ( return (
<Input <Input
onChange={(e) => form.setFieldValue(fieldPath, e.target.value)}/> onChange={(e) => form.setFieldValue(fieldPath, e.target.value)}/>

View File

@@ -2,22 +2,42 @@ import {Kind, parse, print, visit} from "graphql";
import client from "./GraphQLClient"; import client from "./GraphQLClient";
import {gql} from "@apollo/client"; import {gql} from "@apollo/client";
/* eslint-disable no-loop-func */
/**
* The available operators for filtering (string)
* @type {[{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},null,null,null]}
*/
const STRING_OPERATORS = [ const STRING_OPERATORS = [
{value: "_eq", label: "equals"}, {value: "_eq", label: "equals"},
{value: "_neq", label: "does not equal"}, {value: "_neq", label: "does not equal"},
{value: "_like", label: "contains"}, {value: "_like", label: "contains"},
{value: "_nlike", label: "does not contain"}, {value: "_nlike", label: "does not contain"},
{value: "_ilike", label: "contains case-insensitive"}, {value: "_ilike", label: "contains case-insensitive"},
{value: "_nilike", label: "does not contain case-insensitive"} {value: "_nilike", label: "does not contain case-insensitive"},
{value: "_in", label: "in", type: "array"},
{value: "_nin", label: "not in", type: "array"}
]; ];
/**
* The available operators for filtering (numbers)
* @type {[{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},null,null,null]}
*/
const NUMBER_OPERATORS = [ const NUMBER_OPERATORS = [
{value: "_eq", label: "equals"}, {value: "_eq", label: "equals"},
{value: "_neq", label: "does not equal"}, {value: "_neq", label: "does not equal"},
{value: "_gt", label: "greater than"}, {value: "_gt", label: "greater than"},
{value: "_lt", label: "less than"}, {value: "_lt", label: "less than"},
{value: "_gte", label: "greater than or equal"}, {value: "_gte", label: "greater than or equal"},
{value: "_lte", label: "less than or equal"} {value: "_lte", label: "less than or equal"},
{value: "_in", label: "in", type: "array"},
{value: "_nin", label: "not in", type: "array"}
]; ];
/**
* The available operators for sorting
* @type {[{label: string, value: string},{label: string, value: string}]}
*/
const ORDER_BY_OPERATORS = [ const ORDER_BY_OPERATORS = [
{value: "asc", label: "ascending"}, {value: "asc", label: "ascending"},
{value: "desc", label: "descending"} {value: "desc", label: "descending"}
@@ -31,7 +51,6 @@ export function getOrderOperatorsByType() {
return ORDER_BY_OPERATORS; return ORDER_BY_OPERATORS;
} }
/** /**
* Get the available operators for filtering * Get the available operators for filtering
* @param type * @param type
@@ -45,8 +64,6 @@ export function getWhereOperatorsByType(type = 'string') {
return operators[type]; return operators[type];
} }
/* eslint-disable no-loop-func */
/** /**
* Parse a GraphQL query into an AST * Parse a GraphQL query into an AST
* @param query * @param query
@@ -94,8 +111,8 @@ export async function generateTemplate(templateQueryToExecute, templateObject, u
const finalQuery = printQuery(ast); const finalQuery = printQuery(ast);
// commented out for future revision debugging // commented out for future revision debugging
// console.log('Modified Query'); console.log('Modified Query');
// console.log(finalQuery); console.log(finalQuery);
let contextData = {}; let contextData = {};
if (templateQueryToExecute) { if (templateQueryToExecute) {
@@ -109,7 +126,6 @@ export async function generateTemplate(templateQueryToExecute, templateObject, u
return {contextData, useShopSpecificTemplate}; return {contextData, useShopSpecificTemplate};
} }
/** /**
* Apply sorters to the AST * Apply sorters to the AST
* @param ast * @param ast
@@ -174,6 +190,7 @@ export function applySorters(ast, sorters) {
* Apply filters to the AST * Apply filters to the AST
* @param ast * @param ast
* @param filters * @param filters
* @returns {ASTNode}
*/ */
export function applyFilters(ast, filters) { export function applyFilters(ast, filters) {
return visit(ast, { return visit(ast, {
@@ -189,15 +206,56 @@ export function applyFilters(ast, filters) {
topLevel = true; topLevel = true;
} }
if (topLevel) {
// Construct the filter for a top-level application // Construct the filter for a top-level application
const targetFieldName = fieldPath[fieldPath.length - 1]; const targetFieldName = fieldPath[fieldPath.length - 1];
const filterValue = {
let filterValue = createFilterValue(filter);
let filterField = createFilterField(targetFieldName, filter, filterValue);
if (topLevel) {
applyTopLevelFilter(node, fieldPath, filterField);
} else {
applyNestedFilter(node, fieldPath, filterField);
}
});
}
}
});
}
/**
* Create a filter value based on the filter
* @param filter
* @returns {{kind: (Kind|Kind.INT), value}|{kind: Kind.LIST, values: *}}
*/
function createFilterValue(filter) {
if (Array.isArray(filter.value)) {
// If it's an array, create a list value with the array items
return {
kind: Kind.LIST,
values: filter.value.map(item => ({
kind: getGraphQLKind(item),
value: item,
})),
};
} else {
// If it's not an array, use the existing logic
return {
kind: getGraphQLKind(filter.value), kind: getGraphQLKind(filter.value),
value: filter.value, value: filter.value,
}; };
}
}
const nestedFilter = { /**
* Create a filter field based on the target field and filter
* @param targetFieldName
* @param filter
* @param filterValue
* @returns {{kind: Kind.OBJECT_FIELD, name: {kind: Kind.NAME, value}, value: {kind: Kind.OBJECT, fields: [{kind: Kind.OBJECT_FIELD, name: {kind: Kind.NAME, value}, value}]}}}
*/
function createFilterField(targetFieldName, filter, filterValue) {
return {
kind: Kind.OBJECT_FIELD, kind: Kind.OBJECT_FIELD,
name: {kind: Kind.NAME, value: targetFieldName}, name: {kind: Kind.NAME, value: targetFieldName},
value: { value: {
@@ -209,7 +267,15 @@ export function applyFilters(ast, filters) {
}], }],
}, },
}; };
}
/**
* Apply a top-level filter to the AST
* @param node
* @param fieldPath
* @param filterField
*/
function applyTopLevelFilter(node, fieldPath, filterField) {
// Find or create the where argument for the top-level field // Find or create the where argument for the top-level field
let whereArg = node.selectionSet.selections let whereArg = node.selectionSet.selections
.find(selection => selection.name.value === fieldPath[0]) .find(selection => selection.name.value === fieldPath[0])
@@ -245,14 +311,21 @@ export function applyFilters(ast, filters) {
} }
currentField = existingField.value; currentField = existingField.value;
}); });
currentField.fields.push(nestedFilter); currentField.fields.push(filterField);
} else { // Directly under the top level } else { // Directly under the top level
whereArg.value.fields.push(nestedFilter); whereArg.value.fields.push(filterField);
} }
} else { }
/**
* Apply a nested filter to the AST
* @param node
* @param fieldPath
* @param filterField
*/
function applyNestedFilter(node, fieldPath, filterField) {
// Initialize a reference to the current selection to traverse down the AST // Initialize a reference to the current selection to traverse down the AST
let currentSelection = node; let currentSelection = node;
let whereArgFound = false;
// Iterate over the fieldPath, except for the last entry, to navigate the structure // Iterate over the fieldPath, except for the last entry, to navigate the structure
for (let i = 0; i < fieldPath.length - 1; i++) { for (let i = 0; i < fieldPath.length - 1; i++) {
@@ -280,9 +353,7 @@ export function applyFilters(ast, filters) {
// At this point, currentSelection should be the parent field where the filter needs to be applied // At this point, currentSelection should be the parent field where the filter needs to be applied
// Check if the 'where' argument already exists in the current selection // Check if the 'where' argument already exists in the current selection
const whereArg = currentSelection.arguments.find(arg => arg.name.value === 'where'); const whereArg = currentSelection.arguments.find(arg => arg.name.value === 'where');
if (whereArg) { if (!whereArg) {
whereArgFound = true;
} else {
// If not found, create a new 'where' argument for the current selection // If not found, create a new 'where' argument for the current selection
currentSelection.arguments.push({ currentSelection.arguments.push({
kind: Kind.ARGUMENT, kind: Kind.ARGUMENT,
@@ -291,40 +362,8 @@ export function applyFilters(ast, filters) {
}); });
} }
// Assuming the last entry in fieldPath is the field to apply the filter on
const targetField = fieldPath[fieldPath.length - 1];
const filterValue = {
kind: getGraphQLKind(filter.value),
value: filter.value,
};
// Construct the filter field object
const filterField = {
kind: Kind.OBJECT_FIELD,
name: {kind: Kind.NAME, value: targetField},
value: {
kind: Kind.OBJECT,
fields: [{
kind: Kind.OBJECT_FIELD,
name: {kind: Kind.NAME, value: filter.operator},
value: filterValue,
}],
},
};
// Add the filter field to the 'where' clause of the current selection // Add the filter field to the 'where' clause of the current selection
if (whereArgFound) {
whereArg.value.fields.push(filterField);
} else {
// If the whereArg was newly created, find it again (since we didn't store its reference) and add the filter
currentSelection.arguments.find(arg => arg.name.value === 'where').value.fields.push(filterField); currentSelection.arguments.find(arg => arg.name.value === 'where').value.fields.push(filterField);
}
}
});
}
}
});
} }
/** /**
@@ -333,14 +372,17 @@ export function applyFilters(ast, filters) {
* @returns {Kind|Kind.INT} * @returns {Kind|Kind.INT}
*/ */
function getGraphQLKind(value) { function getGraphQLKind(value) {
if (typeof value === 'number') { if (Array.isArray(value)) {
return Kind.LIST;
} else if (typeof value === 'number') {
return value % 1 === 0 ? Kind.INT : Kind.FLOAT; return value % 1 === 0 ? Kind.INT : Kind.FLOAT;
} else if (typeof value === 'boolean') { } else if (typeof value === 'boolean') {
return Kind.BOOLEAN; return Kind.BOOLEAN;
} else if (typeof value === 'string') { } else if (typeof value === 'string') {
return Kind.STRING; return Kind.STRING;
} else if (value instanceof Date) {
return Kind.STRING; // GraphQL does not have a Date type, so we return it as a string
} }
// Extend with more types as needed
} }
/** /**