blixify-ui-web-mcp 0.1.61 → 0.1.62

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.
@@ -259,6 +259,38 @@ const isStatusClosed = liveFilters.some(
259
259
  );
260
260
  ```
261
261
 
262
+ **Filtering on a referenced record's fields**
263
+
264
+ `bareFlexFilterReferences` maps a `reference` or `listReference` column to the collection it points to and that collection's columns. The list and export filters then offer each of those columns as `<Column> › <Field>`, one level deep. A condition on one is sent with a `foreign` lookup (alias `ref_<column>`), which MongoWrapper joins before its `$match`. Hidden columns, `_id`, and reference, file and object columns of the referenced record are not offered.
265
+
266
+ ```tsx
267
+ bareSettings={{
268
+ bareFlexFilters: true,
269
+ bareFlexFilterReferences: {
270
+ lead: { collection: "master-leads", structure: LeadSchema },
271
+ },
272
+ }}
273
+
274
+ // "Lead › Status is Won" is sent as
275
+ // { type: "=", queryId: "ref_lead.status", value: "Won",
276
+ // foreign: [{ collection: "master-leads", localField: "lead", alias: "ref_lead", searchFields: [] }] }
277
+ ```
278
+
279
+ **Filtering on a listObject**
280
+
281
+ `bareFlexFilterListObjects: true` offers each `listObject` column's fields as `<List> › <Field>`, sent on their path (`crew.role`), so a record matches when any row does. It also offers `<List> › Count` with Is, Greater Than, Less Than and Between. A count is sent as whether the list's n-th row exists, so it holds on records where the list was never set:
282
+
283
+ | Count filter | Sent as |
284
+ | --- | --- |
285
+ | Is 3 | `crew.2` exists, `crew.3` does not |
286
+ | Greater Than 2 | `crew.2` exists |
287
+ | Less Than 2 | `crew.1` does not exist |
288
+ | Between 2 and 5 | `crew.1` exists, `crew.5` does not |
289
+
290
+ Two conditions on one list may each be met by a different row. Referenced records and listObjects are not offered on Firebase or `unionCollections` lists.
291
+
292
+ The standalone `FlexFilter` takes both as `extras={{ references, listObjects: true }}`; pass the same object to `renderFlexFilterConditions` when building the query.
293
+
262
294
  **8. newTabUrl (Optional)**
263
295
 
264
296
  Enables right-click → "Open in new tab" on list rows. The component builds the link as `default + recordId + addons`.
@@ -0,0 +1,183 @@
1
+ import { Transition } from "@headlessui/react";
2
+ import axios from "axios";
3
+ import React, { useState } from "react";
4
+ import { StorySection } from "../../storyUtils";
5
+ import { Loading } from "../../display/loading";
6
+ import { DataStructure, DevSettings } from "../dataTemplate/model";
7
+ import { DataTemplateWrapper } from "../dataWrapper";
8
+ import {
9
+ STORY_API_ENDPOINT,
10
+ STORY_NAMES,
11
+ storyWorkspace,
12
+ useStoryBackend,
13
+ } from "../dataWrapper/storyBackend.fixtures";
14
+ import {
15
+ FlexFilter,
16
+ FlexFilterReferences,
17
+ renderFlexFilterConditions,
18
+ } from ".";
19
+ import { FlexFilterData } from "./model";
20
+
21
+ const meta = { title: "Data/Flex Filter/Referenced Record" };
22
+ export default meta;
23
+
24
+ const LEAD_COLLECTION = "demo-ref-leads";
25
+ const REFERRAL_COLLECTION = "demo-ref-referrals";
26
+ const LEAD_STATUSES = ["New", "Quoted", "Won", "Lost"];
27
+
28
+ const leadSchema: DataStructure[] = [
29
+ { id: "name", name: "Name", type: "string" },
30
+ {
31
+ id: "status",
32
+ name: "Status",
33
+ type: "list",
34
+ listSelection: LEAD_STATUSES,
35
+ },
36
+ { id: "systemSize", name: "System Size (kWp)", type: "number" },
37
+ { id: "signedAt", name: "Signed Date", type: "date" },
38
+ ];
39
+
40
+ const referralSchema: DataStructure[] = [
41
+ { id: "referrer", name: "Referrer", type: "string" },
42
+ {
43
+ id: "leadId",
44
+ name: "Lead",
45
+ type: "reference",
46
+ referenceSpaceId: LEAD_COLLECTION,
47
+ referenceLabel: "name",
48
+ },
49
+ {
50
+ id: "payout",
51
+ name: "Payout",
52
+ type: "list",
53
+ listSelection: ["Pending", "Paid"],
54
+ },
55
+ { id: "createdAt", name: "Created At", type: "date" },
56
+ {
57
+ id: "followUps",
58
+ name: "Follow-ups",
59
+ type: "listObject",
60
+ objectData: [
61
+ {
62
+ id: "outcome",
63
+ name: "Outcome",
64
+ type: "list",
65
+ listSelection: ["No Answer", "Callback", "Site Visit"],
66
+ },
67
+ { id: "note", name: "Note", type: "string" },
68
+ ],
69
+ },
70
+ ];
71
+
72
+ const OUTCOMES = ["No Answer", "Callback", "Site Visit"];
73
+
74
+ const references: FlexFilterReferences = {
75
+ leadId: { collection: LEAD_COLLECTION, structure: leadSchema },
76
+ };
77
+
78
+ // INFO: dates spread around today, so "This month" and "Last 30 days" match some
79
+ const handleDaysAgo = (days: number) =>
80
+ new Date(Date.now() - days * 86400000).toISOString();
81
+
82
+ const leads = STORY_NAMES.slice(0, 12).map((name, index) => ({
83
+ _id: `lead-${index + 1}`,
84
+ name,
85
+ status: LEAD_STATUSES[index % LEAD_STATUSES.length],
86
+ systemSize: 4 + (index % 5) * 2,
87
+ signedAt: handleDaysAgo(index * 9),
88
+ }));
89
+
90
+ const referrals = leads.map((eachLead, index) => ({
91
+ _id: `referral-${index + 1}`,
92
+ referrer: STORY_NAMES[(index + 5) % STORY_NAMES.length],
93
+ leadId: eachLead._id,
94
+ payout: index % 3 === 0 ? "Paid" : "Pending",
95
+ createdAt: handleDaysAgo(index * 4),
96
+ // INFO: 0 to 3 follow-ups, so a count filter has something to split
97
+ followUps: Array.from({ length: index % 4 }, (_, row) => ({
98
+ outcome: OUTCOMES[(index + row) % OUTCOMES.length],
99
+ note: `Call ${row + 1}`,
100
+ })),
101
+ }));
102
+
103
+ const extras = { references, listObjects: true };
104
+
105
+ const devSettings: DevSettings = {
106
+ server: "mongoServer",
107
+ dbEndpoint: STORY_API_ENDPOINT,
108
+ imageEndpoint: "",
109
+ uploadEndpoint: "",
110
+ dbAPIToken: "",
111
+ dbUserToken: "",
112
+ schemaEndpoint: "",
113
+ bucketName: "",
114
+ organisationMembers: [],
115
+ };
116
+
117
+ export const ReferencedRecord = () => {
118
+ const mount = useStoryBackend({
119
+ collections: {
120
+ [LEAD_COLLECTION]: leads,
121
+ [REFERRAL_COLLECTION]: referrals,
122
+ },
123
+ });
124
+ const [filters, setFilters] = useState<FlexFilterData[]>([
125
+ { id: "init", columnId: "", operator: "=", value: undefined },
126
+ ]);
127
+ const query = renderFlexFilterConditions(filters, referralSchema, extras);
128
+
129
+ if (!mount) return <Loading />;
130
+
131
+ return (
132
+ <div className="flex flex-col space-y-12">
133
+ <StorySection
134
+ title="Referrals list filtered on the lead and the follow-ups"
135
+ description="Open the filter and pick a Lead › field, e.g. Lead › Status is Won, or Follow-ups › Count / Outcome."
136
+ >
137
+ <DataTemplateWrapper
138
+ title="Referrals"
139
+ type="list"
140
+ collectionId={REFERRAL_COLLECTION}
141
+ schema={referralSchema}
142
+ workspace={storyWorkspace()}
143
+ limit={20}
144
+ organise={{ breadcrumbType: "hidden" }}
145
+ bareSettings={{
146
+ bareNewDTW: true,
147
+ bareFlexFilters: true,
148
+ bareFlexFilterReferences: references,
149
+ bareFlexFilterListObjects: true,
150
+ }}
151
+ />
152
+ </StorySection>
153
+
154
+ <StorySection
155
+ title="Standalone filter (notification audience)"
156
+ description="The same extras on the bare FlexFilter, with the query it builds."
157
+ >
158
+ <div className="flex flex-col">
159
+ <div className="max-w-[700px]">
160
+ <FlexFilter
161
+ structure={referralSchema}
162
+ extras={extras}
163
+ filters={filters}
164
+ onChange={setFilters}
165
+ devSettings={devSettings}
166
+ dbModule={axios}
167
+ variant="bare"
168
+ lib={{ Transition }}
169
+ />
170
+ </div>
171
+ <div className="mt-4 p-4 bg-gray-50 rounded-lg border">
172
+ <p className="text-sm font-semibold mb-2">
173
+ Generated Query Conditions ({query.length})
174
+ </p>
175
+ <pre className="text-xs whitespace-pre-wrap">
176
+ {JSON.stringify(query, null, 2)}
177
+ </pre>
178
+ </div>
179
+ </div>
180
+ </StorySection>
181
+ </div>
182
+ );
183
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blixify-ui-web-mcp",
3
- "version": "0.1.61",
3
+ "version": "0.1.62",
4
4
  "license": "MIT",
5
5
  "main": "lib/mcp/server.js",
6
6
  "bin": {