@stoker-platform/web-app 0.5.225 → 0.5.227

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @stoker-platform/web-app
2
2
 
3
+ ## 0.5.227
4
+
5
+ ### Patch Changes
6
+
7
+ - feat: use equality filter for one-to relation filters
8
+
9
+ ## 0.5.226
10
+
11
+ ### Patch Changes
12
+
13
+ - feat: apply search to list metrics
14
+
3
15
  ## 0.5.225
4
16
 
5
17
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stoker-platform/web-app",
3
- "version": "0.5.225",
3
+ "version": "0.5.227",
4
4
  "type": "module",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "scripts": {
@@ -1541,9 +1541,14 @@ function Collection({
1541
1541
  }
1542
1542
  }
1543
1543
  if (relationList) {
1544
+ const relationListField = getField(fields, relationList.field)
1545
+ const relationListUsesArrayContains = ["ManyToOne", "ManyToMany"].includes(relationListField.type)
1544
1546
  filters.forEach((filter) => {
1545
1547
  if (filter.type === "relation") {
1546
- if (!isPreloadCacheEnabled || filter.field === relationList.field) {
1548
+ if (
1549
+ filter.field === relationList.field ||
1550
+ (!isPreloadCacheEnabled && relationListUsesArrayContains)
1551
+ ) {
1547
1552
  excluded.push(filter.field)
1548
1553
  }
1549
1554
  }
@@ -1553,7 +1558,7 @@ function Collection({
1553
1558
  }
1554
1559
  }
1555
1560
  return excluded
1556
- }, [isPreloadCacheEnabled, cardsConfig, statusField, tab, filters])
1561
+ }, [isPreloadCacheEnabled, cardsConfig, statusField, tab, filters, relationList, fields])
1557
1562
 
1558
1563
  const filtersActive = useMemo(() => {
1559
1564
  return (
package/src/Filters.tsx CHANGED
@@ -103,9 +103,12 @@ export function Filters({
103
103
 
104
104
  const preventChange = isRouteLoadingImmediate.has(location.pathname)
105
105
 
106
- const isArrayOrRelationFilter = useCallback(
106
+ const isArrayContainsFilter = useCallback(
107
107
  (filter: Filter): filter is SelectFilter | RelationFilter => {
108
- if (filter.type === "relation") return true
108
+ if (filter.type === "relation") {
109
+ const fieldSchema = getField(fields, filter.field)
110
+ return ["ManyToOne", "ManyToMany"].includes(fieldSchema.type)
111
+ }
109
112
  if (filter.type === "select") {
110
113
  const fieldSchema = getField(fields, filter.field)
111
114
  return fieldSchema.type === "Array"
@@ -116,11 +119,13 @@ export function Filters({
116
119
  )
117
120
 
118
121
  const isIncludeAssignedActive = useCallback(() => {
119
- if (!isAssigning || !assignable?.includeAssignedInFilters?.length) return false
122
+ if (!isAssigning || !assignable?.includeAssignedInFilters?.length || !relationList) return false
123
+ const relationField = getField(fields, relationList.field)
124
+ if (["OneToOne", "OneToMany"].includes(relationField.type)) return false
120
125
  return assignable.includeAssignedInFilters.some((field) =>
121
126
  filters.some((filter) => filter.type === "select" && filter.field === field && filter.value),
122
127
  )
123
- }, [isAssigning, assignable, filters])
128
+ }, [isAssigning, assignable, filters, relationList, fields])
124
129
 
125
130
  const isMobile = useIsMobile()
126
131
  const someFilterOpen = Object.values(open).some(Boolean)
@@ -213,14 +218,14 @@ export function Filters({
213
218
  }
214
219
  }
215
220
 
216
- const arrayOrRelationFilter = filters.find(
217
- (filter): filter is SelectFilter | RelationFilter => !!filter.value && isArrayOrRelationFilter(filter),
221
+ const arrayContainsFilter = filters.find(
222
+ (filter): filter is SelectFilter | RelationFilter => !!filter.value && isArrayContainsFilter(filter),
218
223
  )
219
- if (arrayOrRelationFilter) {
220
- arrayOrRelationField = arrayOrRelationFilter.field
224
+ if (arrayContainsFilter) {
225
+ arrayOrRelationField = arrayContainsFilter.field
221
226
  }
222
227
  setArrayContainsFilterSet(arrayOrRelationField)
223
- }, [filters, isAssigning, assignable, isPreloadCacheEnabled, isIncludeAssignedActive, isArrayOrRelationFilter])
228
+ }, [filters, isAssigning, assignable, isPreloadCacheEnabled, isIncludeAssignedActive, isArrayContainsFilter])
224
229
 
225
230
  const handleChange = useCallback(
226
231
  (filter: Filter, value: string, type: CollectionField["type"]) => {
@@ -454,19 +459,36 @@ export function Filters({
454
459
  (filter: Filter) => {
455
460
  if (filter.type === "range" || filter.type === "status") return false
456
461
  const fieldSchema = getField(fields, filter.field)
462
+ const relationField = relationList ? getField(fields, relationList.field) : undefined
463
+ const includeAssignedUsesArrayContains =
464
+ isAssigning &&
465
+ !!assignable?.includeAssignedInFilters?.includes(filter.field) &&
466
+ !!relationField &&
467
+ ["ManyToOne", "ManyToMany"].includes(relationField.type)
457
468
  return !!(
458
469
  (!isPreloadCacheEnabled &&
459
470
  arrayContainsFilterSet &&
460
471
  arrayContainsFilterSet !== filter.field &&
461
- (filter.type === "relation" ||
472
+ ((filter.type === "relation" && ["ManyToOne", "ManyToMany"].includes(fieldSchema.type)) ||
462
473
  fieldSchema.type === "Array" ||
463
- (isAssigning && assignable?.includeAssignedInFilters?.includes(filter.field)))) ||
474
+ includeAssignedUsesArrayContains)) ||
464
475
  (isRelationField(fieldSchema) &&
465
476
  connectionStatus === "offline" &&
466
477
  !preloadCacheEnabled(schema.collections[fieldSchema.collection]))
467
478
  )
468
479
  },
469
- [isPreloadCacheEnabled, arrayContainsFilterSet, isRouteLoading, location.pathname, fields],
480
+ [
481
+ isPreloadCacheEnabled,
482
+ arrayContainsFilterSet,
483
+ isRouteLoading,
484
+ location.pathname,
485
+ fields,
486
+ relationList,
487
+ isAssigning,
488
+ assignable,
489
+ connectionStatus,
490
+ schema,
491
+ ],
470
492
  )
471
493
 
472
494
  return (
package/src/List.tsx CHANGED
@@ -1345,9 +1345,9 @@ export function List({
1345
1345
  if (permissions?.Role && (!metric.roles || metric.roles.includes(permissions?.Role))) {
1346
1346
  if (metric.type === "custom" && metric.formula) {
1347
1347
  // eslint-disable-next-line security/detect-object-injection
1348
- values[index] = metric.formula(list).toString() || ""
1348
+ values[index] = metric.formula(searchList).toString() || ""
1349
1349
  } else if (metric.type === "count") {
1350
- let value = (list?.length || 0).toString()
1350
+ let value = (searchList.length || 0).toString()
1351
1351
  if (metric.prefix) {
1352
1352
  value = `${metric.prefix}${value}`
1353
1353
  }
@@ -1361,7 +1361,7 @@ export function List({
1361
1361
  let value: string
1362
1362
  const field = getField(fields, metric.field)
1363
1363
  const fieldCustomization = getFieldCustomization(field, customization)
1364
- list?.forEach((record: StokerRecord) => {
1364
+ searchList.forEach((record: StokerRecord) => {
1365
1365
  if (!metric.field) return
1366
1366
  const value = record[metric.field]
1367
1367
  if (field.type === "Number" && typeof value === "number") {
@@ -1395,7 +1395,7 @@ export function List({
1395
1395
  let value: string
1396
1396
  const field = getField(fields, metric.field)
1397
1397
  const fieldCustomization = getFieldCustomization(field, customization)
1398
- list?.forEach((record: StokerRecord) => {
1398
+ searchList.forEach((record: StokerRecord) => {
1399
1399
  if (!metric.field) return
1400
1400
  const value = record[metric.field]
1401
1401
  if (field.type === "Number" && typeof value === "number") {
@@ -1406,7 +1406,7 @@ export function List({
1406
1406
  total = newTotal
1407
1407
  }
1408
1408
  })
1409
- const average = total / list?.length || 0
1409
+ const average = total / searchList.length || 0
1410
1410
  const currency = tryFunction(fieldCustomization?.admin?.currency)
1411
1411
  if (currency) {
1412
1412
  value = `${currency}${average.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`
@@ -1424,7 +1424,7 @@ export function List({
1424
1424
  } else if (metric.type === "area") {
1425
1425
  if (metric.metricField1) {
1426
1426
  const chartData: MetricValue = []
1427
- list?.forEach((record: StokerRecord) => {
1427
+ searchList.forEach((record: StokerRecord) => {
1428
1428
  if (!record[metric.dateField] || !metric.metricField1) return
1429
1429
  const date = DateTime.fromJSDate((record[metric.dateField] as Timestamp).toDate(), {
1430
1430
  zone: timezone,
@@ -1460,7 +1460,7 @@ export function List({
1460
1460
  const chartData: MetricValue = []
1461
1461
  const dateMap = new Map<string, number>()
1462
1462
 
1463
- list?.forEach((record: StokerRecord) => {
1463
+ searchList.forEach((record: StokerRecord) => {
1464
1464
  if (!record[metric.dateField]) return
1465
1465
  const date = DateTime.fromJSDate((record[metric.dateField] as Timestamp).toDate(), {
1466
1466
  zone: timezone,
@@ -1489,7 +1489,7 @@ export function List({
1489
1489
  }
1490
1490
  })
1491
1491
  return values
1492
- }, [list])
1492
+ }, [list, searchList])
1493
1493
 
1494
1494
  const [timeRange, setTimeRange] = useState<Record<string, string>>({})
1495
1495
 
@@ -152,11 +152,17 @@ export const FiltersProvider: React.FC<FiltersProviderProps> = ({
152
152
  if (includeValueInFilters) {
153
153
  filterValues.push(includeValueInFilters.includeValue as string | number)
154
154
  }
155
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
156
+ const relationField = getField(fields, relationList!.field)
157
+ const assignedConstraint = ["OneToOne", "OneToMany"].includes(relationField.type)
158
+ ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
159
+ where(`${relationList!.field}_Single.id`, "==", relationParent!.id)
160
+ : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
161
+ where(`${relationList!.field}_Array`, "array-contains", relationParent!.id)
155
162
  constraints.push(
156
163
  or(
157
164
  where(filter.field, "in", filterValues),
158
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
159
- where(`${relationList!.field}_Array`, "array-contains", relationParent!.id),
165
+ assignedConstraint,
160
166
  ) as unknown as QueryConstraint,
161
167
  )
162
168
  } else if (isAssigning && includeValueInFilters) {
@@ -169,7 +175,11 @@ export const FiltersProvider: React.FC<FiltersProviderProps> = ({
169
175
  }
170
176
  if (filter.type === "relation") {
171
177
  const field = getField(fields, filter.field)
172
- constraints.push(where(`${field.name}_Array`, "array-contains", filter.value))
178
+ if (["OneToOne", "OneToMany"].includes(field.type)) {
179
+ constraints.push(where(`${field.name}_Single.id`, "==", filter.value))
180
+ } else {
181
+ constraints.push(where(`${field.name}_Array`, "array-contains", filter.value))
182
+ }
173
183
  }
174
184
  })
175
185
  if (!isPreloadCacheEnabled) {
@@ -240,7 +250,11 @@ export const FiltersProvider: React.FC<FiltersProviderProps> = ({
240
250
  }
241
251
  if (filter.type === "relation") {
242
252
  const field = getField(fields, filter.field)
243
- constraints.push([`${field.name}_Array`, "array-contains", filter.value])
253
+ if (["OneToOne", "OneToMany"].includes(field.type)) {
254
+ constraints.push([`${field.name}_Single.id`, "==", filter.value])
255
+ } else {
256
+ constraints.push([`${field.name}_Array`, "array-contains", filter.value])
257
+ }
244
258
  }
245
259
  })
246
260
  if (!isPreloadCacheEnabled) {