@teselagen/ui 0.8.6-beta.2 → 0.8.6-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,10 @@ import {
9
9
  includes,
10
10
  isObject,
11
11
  has,
12
- orderBy
12
+ orderBy,
13
+ endsWith,
14
+ get,
15
+ forEach
13
16
  } from "lodash-es";
14
17
 
15
18
  export function filterLocalEntitiesToHasura(
@@ -27,6 +30,7 @@ export function filterLocalEntitiesToHasura(
27
30
  if (order_by) {
28
31
  filteredRecords = applyOrderBy(filteredRecords, order_by);
29
32
  }
33
+ filteredRecords = restoreEntitiesFromLocalFilter(filteredRecords);
30
34
 
31
35
  // Store the complete filtered and ordered records for pagination info
32
36
  const allFilteredRecords = [...filteredRecords];
@@ -74,7 +78,7 @@ function applyWhereClause(records, where) {
74
78
  return false;
75
79
  }
76
80
  } else {
77
- const value = record[key];
81
+ const value = get(record, key);
78
82
  const conditions = filter[key];
79
83
 
80
84
  // Handle nested object properties
@@ -225,12 +229,131 @@ function applyWhereClause(records, where) {
225
229
  return records.filter(record => applyFilter(record, where));
226
230
  }
227
231
 
228
- function applyOrderBy(records, order_by) {
229
- const keys = Object.keys(order_by);
230
- if (keys.length > 0) {
231
- const field = keys[0];
232
- const direction = order_by[field] === "asc" ? "asc" : "desc";
233
- return orderBy(records, [field], [direction]);
232
+ // takes in an array of records and an order_by clause
233
+ // order_by looks like this: [{ some_field: "asc" }, { some_other_field: "desc" }] or {some_field: "asc"}
234
+ // returns the records sorted by the order_by clause
235
+ function applyOrderBy(records, _order_by) {
236
+ const order_by = isArray(_order_by)
237
+ ? _order_by
238
+ : isEmpty(_order_by)
239
+ ? []
240
+ : [_order_by];
241
+
242
+ if (order_by.length > 0) {
243
+ const orderFuncs = [];
244
+ const ascOrDescArray = [];
245
+
246
+ order_by.forEach(
247
+ ({ path, direction, type, sortFn, getValueToFilterOn, ownProps }) => {
248
+ // Default direction is "desc" if not specified
249
+ direction = direction || "desc";
250
+
251
+ if (sortFn) {
252
+ // Allow sortFn to be a function, a string, or an array of functions/strings
253
+ const sortFnArray = Array.isArray(sortFn) ? sortFn : [sortFn];
254
+
255
+ sortFnArray.forEach(fn => {
256
+ // If fn is a string, treat it as a path to get from the record
257
+ const getter =
258
+ typeof fn === "function"
259
+ ? fn
260
+ : r => get(r, fn);
261
+
262
+ // First handle null check for this function's/string's values
263
+ orderFuncs.push(r => {
264
+ const val = getter(r);
265
+ return val !== null && val !== undefined ? 1 : 0;
266
+ });
267
+ ascOrDescArray.push("desc"); // Always push nulls to the bottom
268
+
269
+ // Then the actual sort function or path getter
270
+ orderFuncs.push(getter);
271
+ ascOrDescArray.push(direction);
272
+ });
273
+ } else if (getValueToFilterOn) {
274
+ // Custom getValue function
275
+ // First handle null check
276
+ orderFuncs.push(r => {
277
+ const val = getValueToFilterOn(r, ownProps);
278
+ return val !== null && val !== undefined ? 1 : 0;
279
+ });
280
+ ascOrDescArray.push("desc"); // Always push nulls to the bottom
281
+
282
+ // Then the actual value getter function
283
+ orderFuncs.push(r => getValueToFilterOn(r, ownProps));
284
+ ascOrDescArray.push(direction);
285
+ } else if (type === "timestamp") {
286
+ // Sort nulls/undefined to the bottom regardless of sort direction
287
+ orderFuncs.push(r => {
288
+ const val = get(r, path);
289
+ // First check if value exists, this ensures nulls go to the bottom
290
+ return val ? 1 : 0;
291
+ });
292
+ ascOrDescArray.push("desc"); // always put nulls at the bottom
293
+
294
+ // Then actual timestamp sorting
295
+ orderFuncs.push(r => {
296
+ const val = get(r, path);
297
+ return val ? new Date(val).getTime() : -Infinity;
298
+ });
299
+ ascOrDescArray.push(direction);
300
+ } else if (path && endsWith(path.toLowerCase(), "id")) {
301
+ // Handle ID fields - sort numerically
302
+ // First handle null check
303
+ orderFuncs.push(r => {
304
+ const val = get(r, path);
305
+ return val !== null && val !== undefined ? 1 : 0;
306
+ });
307
+ ascOrDescArray.push("desc"); // Always push nulls to the bottom
308
+
309
+ // Then the actual ID parsing
310
+ orderFuncs.push(o => {
311
+ const val = get(o, path);
312
+ if (val === null || val === undefined) return -Infinity;
313
+ return parseInt(val, 10) || 0;
314
+ });
315
+ ascOrDescArray.push(direction);
316
+ } else {
317
+ // Default sorting
318
+ // First sort by existence (non-nulls first)
319
+ orderFuncs.push(r => {
320
+ const val = get(r, path);
321
+ return val !== null && val !== undefined ? 1 : 0;
322
+ });
323
+ ascOrDescArray.push("desc"); // Always put nulls at the bottom
324
+
325
+ // Then sort by actual value
326
+ orderFuncs.push(r => {
327
+ const val = get(r, path);
328
+ if (val === null || val === undefined) return -Infinity;
329
+
330
+ // For string sorting, implement natural sort
331
+ if (isString(val)) {
332
+ return val.toLowerCase().replace(/(\d+)/g, num =>
333
+ // Pad numbers with leading zeros for proper natural sort
334
+ num.padStart(10, "0")
335
+ );
336
+ }
337
+ return val;
338
+ });
339
+ ascOrDescArray.push(direction);
340
+ }
341
+ }
342
+ );
343
+
344
+ records = orderBy(records, orderFuncs, ascOrDescArray);
234
345
  }
235
346
  return records;
236
347
  }
348
+
349
+ function restoreEntitiesFromLocalFilter(ents) {
350
+ return ents.map(entity => {
351
+ forEach(entity, (val, key) => {
352
+ if (key.startsWith?.("___original___")) {
353
+ entity[key.slice("___original___".length)] = val;
354
+ delete entity[key];
355
+ }
356
+ });
357
+ return entity;
358
+ });
359
+ }