@@ -25,6 +25,8 @@ import { fetchAttributeValues } from '../../store/sqlMembershipSources.api';
2525import { selectAttributes , selectSource , selectAttributeValues } from '../../store/sqlMembershipSources.slice' ;
2626import { SqlMembershipAttribute , SqlMembershipAttributeValue } from '../../models' ;
2727import { IFilterPart } from '../../models/IFilterPart' ;
28+ import { Group } from '../../models/Group' ;
29+ import { parseGroup , stringifyGroups } from './QuerySerializer' ;
2830
2931export const getClassNames = classNamesFunction < HRQuerySourceStyleProps , HRQuerySourceStyles > ( ) ;
3032
@@ -48,13 +50,6 @@ export const HRQuerySourceBase: React.FunctionComponent<HRQuerySourceProps> = (p
4850 [ key : number ] : IComboBoxOption [ ] ;
4951 } ;
5052
51- interface Group {
52- name : string ;
53- items : IFilterPart [ ] ;
54- children : Group [ ] ;
55- andOr : string ;
56- }
57-
5853 const dispatch = useDispatch < AppDispatch > ( ) ;
5954 const orgLeaderDetails = useSelector ( selectOrgLeaderDetails ) ;
6055 const objectIdEmployeeIdMapping = useSelector ( selectObjectIdEmployeeIdMapping ) ;
@@ -146,266 +141,6 @@ export const HRQuerySourceBase: React.FunctionComponent<HRQuerySourceProps> = (p
146141 }
147142 } , [ children ] ) ;
148143
149- function stringifyGroup ( group : Group , isChild ?: boolean , childIndex ?: number , childrenLength ?: number ) : string {
150- let result = '(' ;
151-
152- result += `${ group . items . map ( ( item , index ) => {
153- if ( index < group . items . length - 1 ) {
154- return item . attribute + ' ' + item . equalityOperator + ' ' + item . value + ' ' + item . andOr ;
155- } else {
156- return item . attribute + ' ' + item . equalityOperator + ' ' + item . value ;
157- }
158- } ) . join ( ' ' ) } `;
159-
160- if ( group . children . length === 0 ) {
161- result += ')' ;
162- }
163-
164- if ( isChild ) {
165- if ( childrenLength && childIndex !== childrenLength - 1 ) result += ` ${ group . andOr } ` ;
166- }
167- else {
168- result += ` ${ group . andOr } ` ;
169- }
170-
171- group . children . forEach ( ( child , index ) => {
172- result += stringifyGroup ( child , true , index , group . children . length ) ;
173- if ( index < group . children . length - 1 ) {
174- result += ' ' ;
175- }
176- } ) ;
177-
178- if ( group . children . length > 0 ) {
179- result += ')' ;
180- result += ` ${ group . children [ group . children . length - 1 ] . andOr } ` ;
181- }
182-
183- return result ;
184- }
185-
186- function stringifyGroups ( groups : Group [ ] ) : string {
187- let result = '' ;
188-
189- groups . forEach ( ( group , index ) => {
190- result += stringifyGroup ( group ) ;
191- if ( index < groups . length - 1 ) {
192- result += ' ' ;
193- }
194- } ) ;
195-
196- return result ;
197- }
198-
199- function parseFilterPart ( part : string ) : IFilterPart {
200- const operators = [ "<=" , ">=" , "<>" , "=" , ">" , "<" ] ;
201- let operatorFound = '' ;
202- let operatorIndex = - 1 ;
203-
204- for ( const operator of operators ) {
205- const index = part . indexOf ( operator ) ;
206- if ( index !== - 1 ) {
207- operatorFound = operator ;
208- operatorIndex = index ;
209- break ;
210- }
211- }
212-
213- if ( operatorIndex === - 1 ) {
214- setFilterTextEnabled ( true ) ;
215- }
216- const attribute = part . slice ( 0 , operatorIndex ) . trim ( ) ;
217- const value = part . slice ( operatorIndex + operatorFound . length ) . trim ( ) ;
218-
219- return {
220- attribute,
221- equalityOperator : operatorFound ,
222- value,
223- andOr : ""
224- } ;
225- }
226-
227- function findPartsOfString ( string : string , substringArray : { currentSegment : string , start : number ; end : number } [ ] ) : { currentSegment : string , start : number ; end : number , andOr : string } [ ] {
228- const output : { currentSegment : string , start : number ; end : number , andOr : "" } [ ] = [ ] ;
229- let lastEnd = 0 ;
230-
231- for ( const substringInfo of substringArray ) {
232- const { currentSegment, start, end } = substringInfo ;
233-
234- // Add the segment between the end of the previous segment and the start of the current segment
235- if ( start > lastEnd ) {
236- output . push ( {
237- currentSegment : string . substring ( lastEnd , start ) ,
238- start : lastEnd ,
239- end : start - 1 ,
240- andOr : ""
241- } ) ;
242- }
243-
244- // Add the current segment
245- output . push ( { currentSegment, start, end, andOr : "" } ) ;
246-
247- // Update lastEnd
248- lastEnd = end + 1 ;
249- }
250-
251- // Add the remaining part of the string after the last segment
252- if ( lastEnd < string . length ) {
253- output . push ( {
254- currentSegment : string . substring ( lastEnd ) ,
255- start : lastEnd ,
256- end : string . length - 1 ,
257- andOr : ""
258- } ) ;
259- }
260-
261- return output ;
262- }
263-
264- function appendAndOr ( a : { currentSegment : string ; start : number ; end : number ; andOr : string ; } [ ] ) {
265- a . forEach ( ( segment , index ) => {
266- let modifiedSegment = segment . currentSegment . trim ( ) ;
267- let startWord = '' ;
268- let endWord = '' ;
269-
270- const lowerCaseSegment = modifiedSegment . toLowerCase ( ) ;
271-
272- if ( lowerCaseSegment . startsWith ( 'and ' ) ) {
273- startWord = 'And' ;
274- modifiedSegment = modifiedSegment . substring ( 4 ) . trim ( ) ;
275- } else if ( lowerCaseSegment . startsWith ( 'or ' ) ) {
276- startWord = 'Or' ;
277- modifiedSegment = modifiedSegment . substring ( 3 ) . trim ( ) ;
278- }
279-
280- if ( lowerCaseSegment . endsWith ( ' and' ) ) {
281- endWord = 'And' ;
282- modifiedSegment = modifiedSegment . substring ( 0 , modifiedSegment . length - 4 ) . trim ( ) ;
283- } else if ( lowerCaseSegment . endsWith ( ' or' ) ) {
284- endWord = 'Or' ;
285- modifiedSegment = modifiedSegment . substring ( 0 , modifiedSegment . length - 3 ) . trim ( ) ;
286- }
287-
288- if ( lowerCaseSegment === 'and' ) {
289- startWord = 'And' ;
290- modifiedSegment = '' ;
291- } else if ( lowerCaseSegment === 'or' ) {
292- startWord = 'Or' ;
293- modifiedSegment = '' ;
294- }
295-
296- if ( startWord !== '' ) {
297- a [ index - 1 ] . andOr = startWord ;
298- }
299- if ( endWord !== '' ) {
300- a [ index ] . andOr = endWord ;
301- }
302-
303- a [ index ] . currentSegment = modifiedSegment ;
304-
305- if ( modifiedSegment === '' ) {
306- a . splice ( index , 1 ) ;
307- } else {
308- a [ index ] . currentSegment = modifiedSegment ;
309- }
310- } ) ;
311- return a ;
312- }
313-
314- function parseGroup ( input : string ) : Group [ ] {
315- const groups : Group [ ] = [ ] ;
316- let subStrings : { currentSegment : string , start : number ; end : number } [ ] = [ ] ;
317- let depth = 0 ;
318- let currentSegment = '' ;
319- let operators : string [ ] = [ ] ;
320-
321- input = input . trim ( ) ;
322- let start : number = 0 ;
323- let end : number = input . length - 1 ;
324-
325- for ( let i = 0 ; i < input . length ; i ++ ) {
326- const char = input [ i ] ;
327-
328- if ( char === '(' ) {
329- if ( depth > 0 ) {
330- currentSegment += char ;
331- }
332- depth ++ ;
333- if ( depth === 1 ) start = i ;
334- } else if ( char === ')' ) {
335- depth -- ;
336- if ( depth === 0 ) {
337- end = i ;
338- subStrings . push ( { currentSegment, start, end} ) ;
339- currentSegment = '' ;
340- } else {
341- currentSegment += char ;
342- }
343- } else if ( depth === 0 && ( input . substr ( i , 3 ) === ' Or' || input . substr ( i , 4 ) === ' And' ) ) {
344- operators . push ( input . substr ( i , input . substr ( i , 4 ) === ' And' ? 4 : 3 ) . trim ( ) ) ;
345- i += operators [ operators . length - 1 ] . length - 1 ;
346- } else if ( depth > 0 ) {
347- currentSegment += char ;
348- }
349- }
350-
351- var allParts = findPartsOfString ( input , subStrings ) ;
352- var allPartsWithAndOr = appendAndOr ( allParts ) ;
353-
354- allPartsWithAndOr . forEach ( ( currentSegment , i ) => {
355- groups . push ( parseSegment ( currentSegment . currentSegment ) ) ;
356- if ( groups [ i ] . children . length > 0 && groups [ i ] . children && groups [ i ] . children [ groups [ i ] . children . length - 1 ] . andOr !== null ) {
357- groups [ i ] . children [ groups [ i ] . children . length - 1 ] . andOr = allPartsWithAndOr [ i ] . andOr ;
358- }
359- else if ( allPartsWithAndOr [ i ] . andOr !== '' ) groups [ i ] . andOr = allPartsWithAndOr [ i ] . andOr ;
360-
361- } ) ;
362- return groups ;
363- }
364-
365- function parseSegment ( segment : string , groupOperator ?: string ) : Group {
366- if ( segment . includes ( '(' ) && segment . includes ( ')' ) ) {
367- let children : Group [ ] = [ ] ;
368- const innerSegments = segment . match ( / \( ( .* ?) \) / g) ?. map ( innerSegment => innerSegment . replace ( / ^ \( | \) $ / g, '' ) ) ;
369- const contentOutsideParentheses = segment . replace ( / \s * \( [ ^ ) ] * \) \s * / g, '||' ) . split ( '||' ) ;
370- if ( innerSegments ) {
371- innerSegments . forEach ( ( innerSegment , index ) => {
372- const childGroup = parseSegment ( innerSegment , contentOutsideParentheses && contentOutsideParentheses . length >= 0 ? contentOutsideParentheses [ index + 1 ] : "" ) ;
373- children . push ( childGroup ) ;
374- } ) ;
375- }
376-
377- let start = segment . indexOf ( '(' ) ;
378- let end = segment . lastIndexOf ( ')' ) ;
379- let remainingSegment = segment . substring ( 0 , start ) + segment . substring ( end + 1 ) ;
380- var match = remainingSegment . match ( / \b ( O r | A n d ) \b / i) ;
381- var operator = match ? match [ 1 ] : null ;
382- remainingSegment = remainingSegment . replace ( / ^ \s * ( O r | A n d ) | \s * ( O r | A n d ) \s * $ / gi, '' ) . trim ( ) ;
383- if ( remainingSegment ) {
384- return {
385- name : '' ,
386- items : parseSegment ( remainingSegment ) . items ,
387- children : children ,
388- andOr : operator ?? ''
389- } ;
390- }
391- }
392- const items = segment . split ( / A n d | O r / gi) . map ( parseFilterPart ) ;
393- const operators = segment . match ( / (?: A n d | O r ) / gi) || [ ] ;
394-
395- items . forEach ( ( item , index ) => {
396- if ( index < items . length - 1 ) {
397- item . andOr = operators [ index ] . trim ( ) ;
398- }
399- } ) ;
400-
401- return {
402- name : '' ,
403- items,
404- children : [ ] ,
405- andOr : groupOperator ?? ''
406- } ;
407- }
408-
409144function setItemsBasedOnGroups ( groups : Group [ ] ) {
410145 let items : IFilterPart [ ] = [ ] ;
411146 groups . forEach ( group => {
@@ -459,9 +194,13 @@ const checkType = (value: string, type: string | undefined): string => {
459194
460195 useEffect ( ( ) => {
461196 if ( props . source . filter && ! groupingEnabled && ( props . source . filter . includes ( "(" ) || props . source . filter . includes ( ")" ) ) ) {
462- const a = parseGroup ( props . source . filter ) ;
463- const b = setItemsBasedOnGroups ( a ) ;
464- setGroups ( a ) ;
197+ const groups = parseGroup ( props . source . filter ) ;
198+ if ( groups . length <= 0 ) {
199+ setFilterTextEnabled ( true ) ;
200+ return ;
201+ }
202+ const b = setItemsBasedOnGroups ( groups ) ;
203+ setGroups ( groups ) ;
465204 setGroupingEnabled ( true ) ;
466205 }
467206 else {
@@ -1891,7 +1630,7 @@ const checkType = (value: string, type: string | undefined): string => {
18911630 disabled = { isEditingExistingJob }
18921631 />
18931632
1894- { ( source . filter && filterTextEnabled ) ?
1633+ { ( source . filter && ( filterTextEnabled || ! attributes ) ) ?
18951634 (
18961635 < > < div className = { classNames . labelContainer } >
18971636 < Label > { strings . HROnboarding . filter } </ Label >
0 commit comments