blixify-ui-web-mcp 0.1.3 → 0.1.18

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.
Files changed (126) hide show
  1. package/lib/components/action/button/README.md +12 -11
  2. package/lib/components/action/button/button.stories.tsx +4 -10
  3. package/lib/components/action/buttonGroup/buttonGroup.stories.tsx +4 -10
  4. package/lib/components/action/iconButton/iconButton.stories.tsx +4 -10
  5. package/lib/components/action/radioList/radioList.stories.tsx +4 -10
  6. package/lib/components/action/slider/slider.stories.tsx +4 -10
  7. package/lib/components/action/textButton/textButton.stories.tsx +4 -10
  8. package/lib/components/action/toggle/toggle.stories.tsx +4 -10
  9. package/lib/components/advanced/collapse/collapse.stories.tsx +4 -10
  10. package/lib/components/advanced/infoWindow/infoWindow.stories.tsx +4 -10
  11. package/lib/components/advanced/popUp/README.md +21 -7
  12. package/lib/components/advanced/popUp/popUp.stories.tsx +4 -10
  13. package/lib/components/auth/bareSignIn/bareSignIn.stories.tsx +30 -10
  14. package/lib/components/auth/bareSignUp/bareSignUp.stories.tsx +4 -10
  15. package/lib/components/auth/signIn/signIn.stories.tsx +4 -10
  16. package/lib/components/auth/signUp/signUp.stories.tsx +4 -10
  17. package/lib/components/chart/areaChart/areaChart.stories.tsx +7 -13
  18. package/lib/components/chart/barChart/barChart.stories.tsx +7 -13
  19. package/lib/components/chart/boxPlotChart/boxPlotChart.stories.tsx +8 -14
  20. package/lib/components/chart/bubbleChart/bubbleChart.stories.tsx +8 -14
  21. package/lib/components/chart/card/card.stories.tsx +4 -10
  22. package/lib/components/chart/ganttChart/ganttChart.stories.tsx +4 -10
  23. package/lib/components/chart/heatmapChart/heatmapChart.stories.tsx +4 -10
  24. package/lib/components/chart/lineChart/lineChart.stories.tsx +4 -10
  25. package/lib/components/chart/pieChart/pieChart.stories.tsx +4 -10
  26. package/lib/components/chart/polarAreaChart/polarAreaChart.stories.tsx +8 -14
  27. package/lib/components/chart/radarChart/radarChart.stories.tsx +4 -10
  28. package/lib/components/chart/radialBar/radialChart.stories.tsx +4 -10
  29. package/lib/components/chart/rangeBarChart/rangeBarChart.stories.tsx +4 -10
  30. package/lib/components/chart/statsCard/statsCard.stories.tsx +4 -10
  31. package/lib/components/chart/treemapChart/treemapChart.stories.tsx +8 -14
  32. package/lib/components/chat/chatAnchor/chatAnchor.stories.tsx +13 -19
  33. package/lib/components/chat/chatBase/README.md +103 -22
  34. package/lib/components/chat/chatBase/chatBase.stories.tsx +652 -52
  35. package/lib/components/chat/chatDashboard/README.md +17 -15
  36. package/lib/components/chat/chatDashboard/chatDashboard.stories.tsx +356 -44
  37. package/lib/components/data/dataGraph/dataGraph.stories.tsx +4 -10
  38. package/lib/components/data/dataReference/dataReference.stories.tsx +4 -10
  39. package/lib/components/data/dataTemplate/README.md +88 -61
  40. package/lib/components/data/dataTemplate/dataModule.stories.tsx +4 -10
  41. package/lib/components/data/dataTemplate/dataTemplate.stories.tsx +4 -10
  42. package/lib/components/data/dataWrapper/README.md +46 -45
  43. package/lib/components/data/dataWrapper/dataWrapper.stories.tsx +258 -489
  44. package/lib/components/data/queryForm/queryForm.stories.tsx +4 -10
  45. package/lib/components/data/readQuery/readQuery.stories.tsx +4 -9
  46. package/lib/components/data/readQueryHoc/readQueryHoc.stories.tsx +4 -10
  47. package/lib/components/data/writeQuery/writeQuery.stories.tsx +4 -8
  48. package/lib/components/design/actionSection/actionSection.stories.tsx +6 -10
  49. package/lib/components/design/banner/banner.stories.tsx +4 -10
  50. package/lib/components/design/bannerAction/bannerAction.stories.tsx +4 -10
  51. package/lib/components/design/blogSection/blogSection.stories.tsx +4 -10
  52. package/lib/components/design/carouselSection/carouselSection.stories.tsx +4 -10
  53. package/lib/components/design/designList/designList.stories.tsx +4 -10
  54. package/lib/components/design/faq/faq.stories.tsx +4 -10
  55. package/lib/components/design/fileGrid/fileGrid.stories.tsx +4 -10
  56. package/lib/components/design/fileViewer/fileViewer.stories.tsx +4 -10
  57. package/lib/components/design/footerSection/footerSection.stories.tsx +4 -10
  58. package/lib/components/design/googleReview/googleReview.stories.tsx +4 -10
  59. package/lib/components/design/heroSection/heroSection.stories.tsx +4 -10
  60. package/lib/components/design/iconList/iconList.stories.tsx +4 -10
  61. package/lib/components/design/imageGallery/imageGallery.stories.tsx +4 -10
  62. package/lib/components/design/imageZoom/imageZoom.stories.tsx +4 -10
  63. package/lib/components/design/leftContent/leftContent.stories.tsx +4 -10
  64. package/lib/components/design/logoCloud/logoCloud.stories.tsx +4 -10
  65. package/lib/components/design/onboarding/onboarding.stories.tsx +4 -10
  66. package/lib/components/design/pricing/pricing.stories.tsx +4 -10
  67. package/lib/components/design/rightContent/rightContent.stories.tsx +4 -10
  68. package/lib/components/design/sectionList/sectionList.stories.tsx +4 -10
  69. package/lib/components/design/teams/teams.stories.tsx +4 -10
  70. package/lib/components/display/alert/alert.stories.tsx +4 -10
  71. package/lib/components/display/calendar/README.md +49 -0
  72. package/lib/components/display/calendar/calendar.stories.tsx +71 -28
  73. package/lib/components/display/clipboard/clipboard.stories.tsx +4 -10
  74. package/lib/components/display/detailList/detailList.stories.tsx +5 -10
  75. package/lib/components/display/drawer/drawer.stories.tsx +5 -10
  76. package/lib/components/display/emptyState/emptyState.stories.tsx +4 -10
  77. package/lib/components/display/feeds/feeds.stories.tsx +4 -10
  78. package/lib/components/display/flexTable/README.md +11 -7
  79. package/lib/components/display/flexTable/flexTable.stories.tsx +114 -11
  80. package/lib/components/display/grid/README.md +28 -27
  81. package/lib/components/display/grid/grid.stories.tsx +4 -10
  82. package/lib/components/display/kanban/kanban.stories.tsx +14 -20
  83. package/lib/components/display/list/README.md +30 -29
  84. package/lib/components/display/list/list.stories.tsx +4 -10
  85. package/lib/components/display/loading/loading.stories.tsx +5 -10
  86. package/lib/components/display/mapView/mapView.stories.tsx +4 -10
  87. package/lib/components/display/modal/modal.stories.tsx +4 -11
  88. package/lib/components/display/notification/notification.stories.tsx +4 -10
  89. package/lib/components/display/progress/progress.stories.tsx +5 -10
  90. package/lib/components/display/stepper/stepper.stories.tsx +5 -10
  91. package/lib/components/display/table/README.md +38 -37
  92. package/lib/components/display/table/table.stories.tsx +8 -14
  93. package/lib/components/display/tag/tag.stories.tsx +4 -10
  94. package/lib/components/input/address/address.stories.tsx +4 -10
  95. package/lib/components/input/canvas/canvas.stories.tsx +4 -10
  96. package/lib/components/input/checkbox/checkbox.stories.tsx +4 -10
  97. package/lib/components/input/datePicker/datePicker.stories.tsx +4 -10
  98. package/lib/components/input/form/form.stories.tsx +4 -10
  99. package/lib/components/input/markdown/markdown.stories.tsx +5 -10
  100. package/lib/components/input/monthYearPicker/monthYearPicker.stories.tsx +4 -10
  101. package/lib/components/input/netPromoterScore/netPromoterScore.stories.tsx +4 -10
  102. package/lib/components/input/rating/rating.stories.tsx +4 -10
  103. package/lib/components/input/select/README.md +30 -29
  104. package/lib/components/input/select/select.stories.tsx +4 -10
  105. package/lib/components/input/signature/signature.stories.tsx +4 -10
  106. package/lib/components/input/textArea/textArea.stories.tsx +4 -10
  107. package/lib/components/input/textInput/textinput.stories.tsx +4 -10
  108. package/lib/components/input/uploadInput/uploadInput.stories.tsx +135 -11
  109. package/lib/components/navigation/bars/bars.stories.tsx +4 -10
  110. package/lib/components/navigation/bottomTab/bottomTab.stories.tsx +5 -10
  111. package/lib/components/navigation/breadcrumb/breadcrumb.stories.tsx +4 -10
  112. package/lib/components/navigation/header/header.stories.tsx +24 -12
  113. package/lib/components/navigation/leftNav/leftNav.stories.tsx +4 -10
  114. package/lib/components/navigation/tabs/tabs.stories.tsx +4 -10
  115. package/lib/components/structure/column/column.stories.tsx +5 -10
  116. package/lib/components/structure/container/container.stories.tsx +5 -10
  117. package/lib/components/structure/link/link.stories.tsx +4 -10
  118. package/lib/components/structure/text/text.stories.tsx +5 -10
  119. package/lib/components/template/authPage/authPage.stories.tsx +6 -5
  120. package/lib/components/template/landingPage/landingPage.stories.tsx +8 -6
  121. package/lib/components/template/productListing/productListing.stories.tsx +7 -13
  122. package/lib/components/template/productOverview/productOverview.stories.tsx +5 -11
  123. package/lib/components/template/shoppingCart/shoppingCart.stories.tsx +6 -12
  124. package/lib/components/template/userHomePage/userHomePage.stories.tsx +7 -6
  125. package/lib/components/template/userRankingDashboard/userRankingDashboard.stories.tsx +5 -11
  126. package/package.json +1 -1
@@ -3,45 +3,27 @@ import {
3
3
  CheckCircleIcon,
4
4
  InformationCircleIcon,
5
5
  } from "@heroicons/react/24/solid";
6
- import { storiesOf } from "@storybook/react";
7
- import moment from "moment";
8
6
  import { useEffect, useRef, useState } from "react";
9
7
  import { Button } from "../../action/button";
10
8
  import { IconButton } from "../../action/iconButton";
9
+ import dayjs from "../../dayjs";
11
10
  import { Alert } from "../../display/alert";
12
11
  import { Loading } from "../../display/loading";
13
12
  import { Tag } from "../../display/tag";
14
13
  import { Tabs } from "../../navigation/tabs";
15
14
  import { StorySection } from "../../storyUtils";
16
- import { Container } from "../../structure/container";
17
- import { DataStructure } from "../dataTemplate/model";
18
15
  import {
19
16
  CalendarSettings,
20
17
  FeedsSettings,
21
18
  KanbanSettings,
22
19
  } from "../dataTemplate/spaceModel";
23
- import { ProjectSchema } from "../testSchema";
20
+ import { VerdantOMChecklistSchema } from "../testSchema";
24
21
  import { DataTemplateWrapper } from "./index";
25
- import README from "./README.md";
26
- const stories = storiesOf("Data", module);
22
+ import React from "react";
23
+ const meta = { title: "Data/Data Wrapper" };
24
+ export default meta;
27
25
 
28
- stories.addParameters({
29
- readme: {
30
- content: README,
31
- },
32
- argTypes: {
33
- status: {
34
- control: { type: "select" },
35
- options: ["Pending Submission", "Pending Review", "Approved", "Rejected"],
36
- description: "Filter by checklist status",
37
- },
38
- },
39
- args: {
40
- status: "Pending Submission",
41
- },
42
- });
43
-
44
- stories.add("Data Wrapper", () => {
26
+ export const DataWrapper = () => {
45
27
  const [mount, setMount] = useState<boolean>(false);
46
28
  const dataRefreshRef: any = useRef(null);
47
29
  const [statusFilter] = useState<string>("all");
@@ -88,8 +70,8 @@ stories.add("Data Wrapper", () => {
88
70
  }
89
71
  if (listLayout === "calendar") {
90
72
  viewSettings.calendarSettings = {
91
- title: "researchTitle",
92
- startDate: "createdAt",
73
+ title: "clientName",
74
+ startDate: "date",
93
75
  };
94
76
  }
95
77
  return viewSettings;
@@ -101,7 +83,7 @@ stories.add("Data Wrapper", () => {
101
83
  {data.map((eachItem: any, index: number) => {
102
84
  return (
103
85
  <div className="p-4 rounded-lg bg-gray-50 truncate text-sm text-gray-600">
104
- {eachItem?.researchTitle || `Item ${index}`}
86
+ {eachItem?.clientName || `Item ${index}`}
105
87
  </div>
106
88
  );
107
89
  })}
@@ -132,19 +114,19 @@ stories.add("Data Wrapper", () => {
132
114
  <div className="mb-6" />
133
115
  <DataTemplateWrapper
134
116
  ref={dataRefreshRef}
135
- title={"KPJ Projects"}
136
- description="Manage all your projects here"
117
+ title={"Verdant O&M Checklist"}
118
+ description="Manage O&M maintenance checklists"
137
119
  type={viewType}
138
120
  id={selectedId}
139
- collectionId={"kpj-projects"}
140
- schema={ProjectSchema}
121
+ collectionId={"verdant-om-maintenance-checklist"}
122
+ schema={VerdantOMChecklistSchema}
141
123
  workspace={{
142
124
  apiEndpointType: "mongoDB",
143
- apiEndpoint: `${dtwBaseUrl}/api/data`,
125
+ apiEndpoint: `${dtwBaseUrl}/api/mongoData`,
144
126
  apiEndpointToken: "",
145
127
  apiUserToken: "",
146
128
  assetEndpoint:
147
- "https://firebasestorage.googleapis.com/v0/b/verdant-dev.firebasestorage.app/o/",
129
+ "https://firebasestorage.googleapis.com/v0/b/verdant-solar-dev.appspot.com/o/",
148
130
  assetUploadEndpoint: `${dtwBaseUrl}/api/asset`,
149
131
  }}
150
132
  edit={{
@@ -154,9 +136,8 @@ stories.add("Data Wrapper", () => {
154
136
  deleteBtn: true,
155
137
  }}
156
138
  bareSettings={{
157
- bareOffline: false, //INFO: If set to true, form data will be saved into device's indexedDB and users have to submit manually once internet connection resumes
139
+ bareOffline: false,
158
140
  bareListComponent:
159
- // INFO: Render custom component when type is list (table view, list view, grid view etc)
160
141
  listLayout === "customList"
161
142
  ? (data) => renderCustomListComponent(data)
162
143
  : undefined,
@@ -169,7 +150,7 @@ stories.add("Data Wrapper", () => {
169
150
  size="small"
170
151
  />
171
152
  );
172
- } // override default UI with custom component where columnId matches key when type is list (table, list, grid etc)
153
+ }
173
154
  },
174
155
  bareListActionBtns: (data) => [
175
156
  <IconButton
@@ -181,12 +162,9 @@ stories.add("Data Wrapper", () => {
181
162
  e.stopPropagation();
182
163
  console.log(data);
183
164
  }}
184
- />, // render additional custom button in each table row
185
- ],
186
- bareListFilterQuery: [
187
- // INFO: Add query to filter data when type is list (table view, grid view, grid view etc)
188
- // { type: "=", queryId: "status", value: "Approved" },
165
+ />,
189
166
  ],
167
+ bareListFilterQuery: [],
190
168
  bareListGridComponent: (data: any) => {
191
169
  if (data?.status === "Approved") {
192
170
  return (
@@ -194,45 +172,43 @@ stories.add("Data Wrapper", () => {
194
172
  Custom Component Below Main Content
195
173
  </div>
196
174
  );
197
- } // add custom content below the grid item (will not overide the default UI)
175
+ }
198
176
  },
199
- bareListGridColClassName: "grid-cols-2 md:grid-cols-4 gap-4", // override the default grid column, row and gap classnames
177
+ bareListGridColClassName: "grid-cols-2 md:grid-cols-4 gap-4",
200
178
  bareListGridTags: (data) => {
201
179
  const tags: string[] = [];
202
180
  if (data?.status) tags.push(data.status);
203
181
  if (data?.researchTitleAbbreviation)
204
182
  tags.push(data.researchTitleAbbreviation);
205
-
206
- return tags; // add small tags above grid item's title
183
+ return tags;
207
184
  },
208
185
  bareListGridFallbackImg: (data) => {
209
186
  if (data?.status === "Approved") {
210
187
  return "https://images.unsplash.com/photo-1553877522-43269d4ea984?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3zD";
211
188
  } else
212
189
  return "https://images.unsplash.com/photo-1584644769698-4762ca337c17?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D";
213
- }, // fallback image in case value for image column is not present
190
+ },
214
191
  bareListGridRibbonText: (data) => {
215
192
  if (data?.status?.includes("Review")) {
216
193
  return "Review";
217
194
  }
218
- return ""; // add a ribbon on the top left corner of the grid item
195
+ return "";
219
196
  },
220
197
  bareListHandleData: (data) => {
221
- console.log("Available data in current date: ", data); // returns all the data when type (view type) is list
198
+ console.log("Available data in current date: ", data);
222
199
  },
223
200
  bareReadComponent: () => (
224
201
  <Alert title="Custom Alert in Read View" />
225
- ), // Custom additional component in read view
226
- bareReadComponentPos: "top", // position custom addition read view component to the top
227
- bareReadComponentHide: false, // only set to true if want to override default read view with bareReadComponent
202
+ ),
203
+ bareReadComponentPos: "top",
204
+ bareReadComponentHide: false,
228
205
  bareReadDescription: (key, value) => {
229
- // INFO: override default UI with custom component where columnId matches key in read view
230
206
  if (key === "status" && value) {
231
207
  return <Tag tagText={value} />;
232
208
  }
233
209
  },
234
210
  bareReadHandleData: (data) => {
235
- console.log("Data from read view: ", data); // returns data when enter read view
211
+ console.log("Data from read view: ", data);
236
212
  },
237
213
  bareReferenceDel: [
238
214
  {
@@ -241,11 +217,9 @@ stories.add("Data Wrapper", () => {
241
217
  columnId: "",
242
218
  schema: [],
243
219
  },
244
- ], // INFO: This is to prevent deletion of data if there is a reference input in schema and the reference column points to existing collection in database
245
- bareUpdateMobileView: true, //INFO: This will render form input in compact mobile view
220
+ ],
221
+ bareUpdateMobileView: true,
246
222
  bareUpdateActionComponent:
247
- // INFO: Add custom component on top right corner, normally used to add buttons
248
- // NOTE: This will overide the default action buttons (except for Delete button). Add those buttons back manually
249
223
  viewType === "read"
250
224
  ? (data) => {
251
225
  return (
@@ -264,16 +238,14 @@ stories.add("Data Wrapper", () => {
264
238
  }
265
239
  : undefined,
266
240
  bareUpdatePrefill: () => {
267
- // INFO: prefill column values. These columns will no longer be visible on the form. Good for values that cannot be edited by users
268
241
  return [
269
242
  {
270
243
  key: "updatedAt",
271
- value: moment().toDate(),
244
+ value: dayjs().toDate(),
272
245
  },
273
246
  ];
274
247
  },
275
248
  bareUpdateComponent: () => {
276
- // INFO: add additional component to below form in update view
277
249
  return (
278
250
  <Alert
279
251
  title="Hi there!"
@@ -281,39 +253,22 @@ stories.add("Data Wrapper", () => {
281
253
  />
282
254
  );
283
255
  },
284
- bareUpdateComponentAbove: true, // INFO: render bareUpdateComponent above the form. Set to false to render it below form
256
+ bareUpdateComponentAbove: true,
285
257
  bareUpdateIndividualComponent: (
286
258
  _columnId: string,
287
259
  _ref: any,
288
260
  _selectedData: any,
289
261
  _setSelectedData: (data: any) => void,
290
262
  ) => {
291
- // INFO: Override default input with custom input, value state will be pass back to the form
292
- // Example:
293
- // if (columnId === "column X") {
294
- // return (
295
- // <TextInput
296
- // ref={ref}
297
- // label=""
298
- // placeholder=""
299
- // type="text"
300
- // value={selectedData.columnId}
301
- // onChange={(e) => setSelectedData(e.target.value)}
302
- // />
303
- // );
304
- // }
305
- return <div></div>;
263
+ return null;
306
264
  },
307
265
  bareUpdateHandleData: (data) => {
308
- // INFO: returns data when enter update view
309
266
  console.log("Update view data: ", data);
310
267
  },
311
268
  bareUpdateTabId: (tabId) => {
312
- // INFO: Returns the current tab in update view
313
269
  console.log("Updating data from tab ", tabId);
314
270
  },
315
271
  bareUpdateTabComponent: (tabIds) => {
316
- // INFO: Add icon to tab or skip tab in update view
317
272
  return tabIds.map((eachTabId, index: number) => {
318
273
  const icon =
319
274
  Math.floor(index / 2) % 2 === 0 ? (
@@ -321,7 +276,6 @@ stories.add("Data Wrapper", () => {
321
276
  ) : (
322
277
  <InformationCircleIcon className="w-5 h-5 text-yellow-500" />
323
278
  );
324
-
325
279
  return {
326
280
  tabId: eachTabId,
327
281
  icon,
@@ -329,46 +283,26 @@ stories.add("Data Wrapper", () => {
329
283
  };
330
284
  });
331
285
  },
332
- bareUpdateColumnType: "two", // control how many inputs to be dispalyed per row in update view (default is 2, small browser width default to 1)
286
+ bareUpdateColumnType: "two",
333
287
  bareUpdateHandlePreComplete: (data) => {
334
- /**
335
- * Example use cases:
336
- * 1. Validate data before submission. Return true if validation pass, otherwise return false to prevent submission
337
- * 2. Override column value in the data
338
- */
339
-
340
- // Example 1: Validate data
341
- // if(validation passes){
342
- // return true
343
- // }else return false
344
-
345
- // Example 2: Overrite column value
346
- // if(condtion X){
347
- // data.columnId = formatted value
348
- // }
349
-
350
288
  return data;
351
289
  },
352
- bareUpdateHandleComplete: async (data) => {
353
- console.log("Form data to be submitted: ", data);
354
- // INFO: Use this to override the default form submission (write request to databse)
355
- // await handleCustomFormSubmit()
356
- },
290
+ // bareUpdateHandleComplete: async (data) => {
291
+ // console.log("Form data to be submitted: ", data);
292
+ // },
357
293
  bareUpdateHandlePostComplete: async (data) => {
358
294
  console.log("Form data after submission: ", data);
359
- // INFO: Use this to perform other actions once form data is submitted (wrote to database)
360
- // await handleCustomPostFormSubmit()
361
295
  },
362
296
  }}
363
297
  organise={{
364
- sort: { sortId: "createdAt", type: "desc" as const }, // sort list view data
365
- hideSearch: false, // set to false to enable default search input in list view
366
- filterComponentNewRow: true, // avoid crowding for filter inputs in list view
367
- breadcrumbType: "button", // shows a "Back" button in read view and update view
368
- tabAlign: "left", // shows form input categories (based on groupId from models in schema) in update view
369
- showExport: true, // enable export CSV, Import CSV, Print etc. Only when type is list (table view, grid view, list view etc)
298
+ sort: { sortId: "createdAt", type: "desc" as const },
299
+ hideSearch: false,
300
+ filterComponentNewRow: true,
301
+ breadcrumbType: "button",
302
+ tabAlign: "left",
303
+ showExport: true,
370
304
  exportSettings: {
371
- enableExportReadPDF: true, // enable export read view page UI, blob will be generated
305
+ enableExportReadPDF: true,
372
306
  exportReadPDFDimensions: {
373
307
  forceDesktopView: true,
374
308
  captureWidth: 1440,
@@ -388,14 +322,13 @@ stories.add("Data Wrapper", () => {
388
322
  },
389
323
  },
390
324
  onExportReadPDFBlob: async (blob) => {
391
- const fileName = `Export-${moment().format(
325
+ const fileName = `Export-${dayjs().format(
392
326
  "YYYY-MM-DD-HH-mm-ss",
393
327
  )}.pdf`;
394
328
  const file = new File([blob], fileName, {
395
329
  type: "application/pdf",
396
330
  });
397
331
  console.log("Read View Export: ", file);
398
- // Process blob to generate PDF
399
332
  },
400
333
  },
401
334
  }}
@@ -403,24 +336,13 @@ stories.add("Data Wrapper", () => {
403
336
  additionalQuery={[
404
337
  {
405
338
  type: "list",
406
- value: "sustainableDevelopmentGoals",
407
- options: [
408
- "No poverty",
409
- "Zero Hunger",
410
- "Good Health and Well Being",
411
- ],
412
- filterLabel: "Development Goals",
413
- },
414
- {
415
- type: "reference",
416
- value: "grantId",
417
- spaceId: "kpj-grants",
418
- label: "title",
419
- filterLabel: "Grant ID",
339
+ value: "typeOfInverter",
340
+ options: ["String Inverter", "Microinverter"],
341
+ filterLabel: "Inverter Type",
420
342
  },
421
343
  {
422
344
  type: "range",
423
- value: "createdAt",
345
+ value: "date",
424
346
  },
425
347
  ]}
426
348
  limit={10}
@@ -429,403 +351,250 @@ stories.add("Data Wrapper", () => {
429
351
  </div>
430
352
  </StorySection>
431
353
  );
432
- });
354
+ };
433
355
 
434
- stories.add("Data Wrapper Union", () => {
356
+ // INFO: Story to test imageTagToggleMode + imageHideEnabled across 3 role views:
357
+ // - Installer: no props, sees all photos
358
+ // - Customer: imageHideEnabled=true, hidden photos filtered out in read view
359
+ // - PE: imageTagToggleMode=true, eye icon to toggle hidden flag, read shows all
360
+ export const ImageToggleRoleTest = () => {
435
361
  const [mount, setMount] = useState<boolean>(false);
436
362
  const dataRefreshRef: any = useRef(null);
437
- const [statusFilter, setStatusFilter] = useState<string>("all");
438
- const [startDate, setStartDate] = useState<string>("");
439
- const [endDate, setEndDate] = useState<string>("");
440
- const [installerFilter, setInstallerFilter] = useState<string>("Wai Seng");
441
363
  const [viewType, setViewType] = useState<"list" | "read" | "update">("list");
442
364
  const [selectedId, setSelectedId] = useState<string | undefined>(undefined);
365
+ const [role, setRole] = useState<"installer" | "customer" | "pe">(
366
+ "installer",
367
+ );
368
+ const dtwBaseUrl = "http://localhost:3000";
443
369
 
444
370
  useEffect(() => {
445
371
  setMount(true);
446
372
  }, []);
447
373
 
448
374
  useEffect(() => {
449
- if (mount && dataRefreshRef.current) {
450
- dataRefreshRef.current.handleRefresh();
451
- }
452
- }, [installerFilter, statusFilter, startDate, endDate, mount]);
375
+ if (mount && dataRefreshRef.current) dataRefreshRef.current.handleRefresh();
376
+ }, [mount]);
453
377
 
454
- const handleNavigate = (type: any, id: any, viewId: any) => {
378
+ const handleNavigate = (type: any, id: any) => {
455
379
  setViewType(type);
456
380
  setSelectedId(id);
457
381
  };
458
382
 
459
- const handleBackToList = () => {
460
- setViewType("list");
461
- setSelectedId(undefined);
462
- };
383
+ if (!mount) return <Loading />;
463
384
 
464
- const getUnionCollectionsConfig = () => {
465
- const basePersonQuery: any[] = [];
385
+ // INFO: schema varies per role — only the sitePhotos field differs
386
+ const buildSchema = () => {
387
+ const base = [
388
+ {
389
+ id: "clientName",
390
+ name: "Client Name",
391
+ type: "string" as const,
392
+ groupId: "Basic Info",
393
+ tabId: "A. Basic Info",
394
+ },
395
+ {
396
+ id: "date",
397
+ name: "Service Date",
398
+ type: "date" as const,
399
+ groupId: "Basic Info",
400
+ tabId: "A. Basic Info",
401
+ },
402
+ ];
466
403
 
467
- if (installerFilter.length > 0) {
468
- basePersonQuery.push({
469
- type: "search",
470
- searchIds: ["installer", "inspector"],
471
- value: installerFilter,
472
- });
473
- }
404
+ const photoField: any = {
405
+ id: "sitePhotos",
406
+ name: "Site Photos",
407
+ type: "multipleImage",
408
+ imageCollectionName: "verdant-om-maintenance-checklist",
409
+ imageTagOptions: ["Before", "After", "Panel", "Inverter", "DB", "Defect"],
410
+ imageTagNewOption: true,
411
+ optional: true,
412
+ groupId: "Site Photos",
413
+ tabId: "B. Photos",
414
+ };
474
415
 
475
- if (statusFilter !== "all") {
476
- basePersonQuery.push({
477
- queryId: "status",
478
- type: "=",
479
- value: statusFilter,
480
- });
416
+ if (role === "pe") {
417
+ // INFO: PE can toggle hide/show per image; read view shows all (no imageHideEnabled)
418
+ photoField.imageTagEdit = true;
419
+ photoField.imageTagToggleMode = true;
420
+ } else if (role === "customer") {
421
+ // INFO: Customer read view respects hidden flag — hidden images filtered out
422
+ photoField.imageHideEnabled = true;
481
423
  }
424
+ // INFO: installer — no extra props, all images always visible
482
425
 
483
- const getDateQuery = (dateField: string) => {
484
- if (startDate || endDate) {
485
- return {
486
- queryId: dateField,
487
- value:
488
- startDate && endDate
489
- ? [
490
- new Date(startDate).toISOString(),
491
- new Date(endDate).toISOString(),
492
- ]
493
- : new Date(startDate).toISOString(),
494
- type: startDate && endDate ? "><" : ">",
495
- };
496
- }
497
- return null;
498
- };
499
-
500
- return {
501
- unionCollections: [
502
- {
503
- collection: "verdant-deliveries",
504
- source: "dynamic",
505
- sourceLogic: {
506
- $cond: {
507
- if: { $eq: ["$type", "om"] },
508
- then: "O&M Maintenance",
509
- else: "PV Delivery",
510
- },
511
- },
512
- query: [
513
- {
514
- queryId: "type",
515
- type: "in",
516
- value: ["om", "pv"],
517
- },
518
- ...(getDateQuery("proposed_date")
519
- ? [
520
- {
521
- queryId: "proposed_date",
522
- type: getDateQuery("proposed_date")!.type,
523
- value: getDateQuery("proposed_date")!.value,
524
- orQuery: true,
525
- orQueryGroupId: "deliveryDate",
526
- },
527
- ]
528
- : []),
529
- ...(getDateQuery("tent_installation_date")
530
- ? [
531
- {
532
- queryId: "tent_installation_date",
533
- type: getDateQuery("tent_installation_date")!.type,
534
- value: getDateQuery("tent_installation_date")!.value,
535
- orQuery: true,
536
- orQueryGroupId: "deliveryDate",
537
- },
538
- ]
539
- : []),
540
- ...basePersonQuery,
541
- ].filter(Boolean),
542
- projection: {
543
- _id: 1,
544
- mondayItemId: 1,
545
- clientName: 1,
546
- date: 1,
547
- createdAt: 1,
548
- type: 1,
549
- status: 1,
550
- installer: 1,
551
- name: 1,
552
- phone: 1,
553
- location: 1,
554
- proposed_date: 1,
555
- tent_installation_date: 1,
556
- tent_service_date: 1,
557
- checklist_status: 1,
558
- },
559
- addFields: {
560
- sortDate: {
561
- $ifNull: [
562
- {
563
- $cond: {
564
- if: { $eq: ["$type", "om"] },
565
- then: "$proposed_date",
566
- else: "$tent_installation_date",
567
- },
568
- },
569
- "$createdAt",
570
- ],
571
- },
572
- },
573
- },
574
- {
575
- collection: "verdant-service-tickets",
576
- source: "dynamic",
577
- sourceLogic: {
578
- $cond: {
579
- if: { $eq: ["$category", "Installation Defect"] },
580
- then: "Installation Defect",
581
- else: "Critical Load",
582
- },
583
- },
584
- query: [
585
- {
586
- queryId: "category",
587
- type: "in",
588
- value: ["Installation Defect", "Critical Load"],
589
- },
590
- ...(getDateQuery("tent_service_date")
591
- ? [
592
- {
593
- queryId: "tent_service_date",
594
- type: getDateQuery("tent_service_date")!.type,
595
- value: getDateQuery("tent_service_date")!.value,
596
- },
597
- ]
598
- : []),
599
- ...basePersonQuery,
600
- ].filter(Boolean),
601
- projection: {
602
- _id: 1,
603
- mondayItemId: 1,
604
- clientName: 1,
605
- date: 1,
606
- createdAt: 1,
607
- category: 1,
608
- status: 1,
609
- installer: 1,
610
- name: 1,
611
- phone: 1,
612
- location: 1,
613
- tent_service_date: 1,
614
- checklist_status: 1,
615
- },
616
- addFields: {
617
- sortDate: {
618
- $ifNull: ["$tent_service_date", "$createdAt"],
619
- },
620
- },
621
- },
622
- ],
623
- sort: { sortId: "sortDate", type: "desc" as const },
624
- };
426
+ return [...base, photoField];
625
427
  };
626
428
 
627
- if (!mount) {
628
- return <Loading />;
629
- }
630
-
631
429
  return (
632
- <Container bgColor="bg-white">
633
- <div className="mb-4 flex flex-wrap items-end gap-4">
634
- {viewType !== "list" && (
635
- <button
636
- onClick={handleBackToList}
637
- className="px-3 py-1 text-sm bg-gray-200 hover:bg-gray-300 rounded-md"
638
- >
639
- ← Back to List
640
- </button>
641
- )}
642
- {viewType === "list" && (
643
- <>
644
- <div className="flex flex-col">
645
- <label className="text-sm text-gray-700 mb-1">
646
- Filter by Installer
647
- </label>
648
- <select
649
- className="border border-gray-300 rounded-md px-2 py-1 text-sm"
650
- value={installerFilter}
651
- onChange={(e) => setInstallerFilter(e.target.value)}
652
- >
653
- <option value="Wai Seng">Wai Seng</option>
654
- <option value="John Doe">John Doe</option>
655
- <option value="Jane Smith">Jane Smith</option>
656
- </select>
657
- </div>
658
- <div className="flex flex-col">
659
- <label className="text-sm text-gray-700 mb-1">
660
- Filter by Status
661
- </label>
662
- <select
663
- className="border border-gray-300 rounded-md px-2 py-1 text-sm"
664
- value={statusFilter}
665
- onChange={(e) => setStatusFilter(e.target.value)}
666
- >
667
- <option value="all">All</option>
668
- <option value="Pending to set">Pending to set</option>
669
- <option value="Confirmed">Confirmed</option>
670
- </select>
671
- </div>
672
- <div className="flex flex-col">
673
- <label className="text-sm text-gray-700 mb-1">Start Date</label>
674
- <input
675
- type="date"
676
- className="border border-gray-300 rounded-md px-2 py-1 text-sm"
677
- value={startDate}
678
- onChange={(e) => setStartDate(e.target.value)}
679
- />
680
- </div>
681
- <div className="flex flex-col">
682
- <label className="text-sm text-gray-700 mb-1">End Date</label>
683
- <input
684
- type="date"
685
- className="border border-gray-300 rounded-md px-2 py-1 text-sm"
686
- value={endDate}
687
- onChange={(e) => setEndDate(e.target.value)}
688
- />
689
- </div>
690
- </>
691
- )}
692
- {viewType !== "list" && (
693
- <div className="flex flex-col">
694
- <label className="text-sm text-gray-700 mb-1">Current View:</label>
695
- <div className="text-xs text-gray-600">
696
- {viewType} - ID: {selectedId}
697
- </div>
698
- </div>
699
- )}
430
+ <StorySection
431
+ title="Image Toggle Role Test (imageTagToggleMode + imageHideEnabled)"
432
+ description="Switch roles to test: Installer sees all photos. PE gets eye icon to hide/show per image (persisted to data). Customer read view filters out hidden images."
433
+ >
434
+ <div className="flex gap-3 mb-4">
435
+ {(["installer", "customer", "pe"] as const).map((r) => (
436
+ <Button
437
+ key={r}
438
+ text={r.toUpperCase()}
439
+ type={role === r ? "dark" : "light"}
440
+ onClick={() => {
441
+ setRole(r);
442
+ setViewType("list");
443
+ setSelectedId(undefined);
444
+ }}
445
+ />
446
+ ))}
447
+ <span className="text-sm text-gray-500 self-center ml-2">
448
+ Current role: <strong>{role}</strong>
449
+ {role === "pe" && " — eye icon on each photo to hide/show"}
450
+ {role === "customer" && " — hidden photos filtered out in read view"}
451
+ {role === "installer" && " — all photos always visible"}
452
+ </span>
700
453
  </div>
701
454
  <DataTemplateWrapper
702
455
  ref={dataRefreshRef}
703
- title={"Union Collections Demo"}
704
- description="Dynamic unionCollections combining deliveries & service tickets with installer filtering"
456
+ title={"O&M Checklist — Image Toggle Test"}
457
+ description="Test imageTagToggleMode and imageHideEnabled across roles"
705
458
  type={viewType}
706
459
  id={selectedId}
707
- collectionId="verdant-deliveries"
708
- schema={
709
- [
710
- {
711
- id: "source",
712
- name: "Source",
713
- type: "string",
714
- hideForm: true,
715
- },
716
- {
717
- id: "type",
718
- name: "Type",
719
- type: "string",
720
- optional: true,
721
- hideForm: true,
722
- },
723
- {
724
- id: "name",
725
- name: "Client Name",
726
- type: "string",
727
- optional: true,
728
- hideForm: true,
729
- },
730
- {
731
- id: "status",
732
- name: "Status",
733
- type: "string",
734
- optional: true,
735
- hideForm: true,
736
- },
737
- {
738
- id: "category",
739
- name: "Category",
740
- type: "string",
741
- optional: true,
742
- hideForm: true,
743
- },
744
- {
745
- id: "proposed_date",
746
- name: "Proposed Date",
747
- type: "date",
748
- optional: true,
749
- hideForm: true,
750
- },
751
- {
752
- id: "tent_service_date",
753
- name: "Service Date",
754
- type: "date",
755
- optional: true,
756
- hideForm: true,
757
- },
758
- {
759
- id: "date",
760
- name: "Date",
761
- type: "date",
762
- optional: true,
763
- hideForm: true,
764
- },
765
- {
766
- id: "location",
767
- name: "Location",
768
- type: "string",
769
- optional: true,
770
- hideForm: true,
771
- },
772
- {
773
- id: "installer",
774
- name: "Installer",
775
- type: "string",
776
- optional: true,
777
- hideForm: true,
778
- },
779
- {
780
- id: "checklist_status",
781
- name: "Checklist Status",
782
- type: "string",
783
- optional: true,
784
- hideForm: true,
785
- },
786
- {
787
- id: "phone",
788
- name: "Phone",
789
- type: "string",
790
- optional: true,
791
- hideForm: true,
792
- },
793
- {
794
- id: "mondayItemId",
795
- name: "Monday Item ID",
796
- type: "string",
797
- optional: true,
798
- hideForm: true,
799
- },
800
- {
801
- id: "clientName",
802
- name: "Client",
803
- type: "string",
804
- optional: true,
805
- hideForm: true,
806
- },
807
- ] as DataStructure[]
808
- }
460
+ collectionId={"verdant-om-maintenance-checklist"}
461
+ schema={buildSchema()}
809
462
  workspace={{
810
463
  apiEndpointType: "mongoDB",
811
- apiEndpoint: "http://localhost:3000/api/mongoData",
464
+ apiEndpoint: `${dtwBaseUrl}/api/data`,
812
465
  apiEndpointToken: "",
813
- assetUploadEndpoint: "http://localhost:3000/api/asset",
814
466
  apiUserToken: "",
467
+ assetEndpoint:
468
+ "https://firebasestorage.googleapis.com/v0/b/verdant-dev.firebasestorage.app/o/",
469
+ assetUploadEndpoint: `${dtwBaseUrl}/api/asset`,
470
+ }}
471
+ edit={{
472
+ allEditable: true,
473
+ disabledUpdate: false,
474
+ disableBatchUpdate: false,
475
+ deleteBtn: true,
815
476
  }}
816
477
  bareSettings={{
817
- bareUpdateMobileView: true,
478
+ bareUpdateActionComponent:
479
+ viewType === "read"
480
+ ? (data) => (
481
+ <Button
482
+ type="normal"
483
+ text="Update"
484
+ onClick={() => {
485
+ setSelectedId(data._id);
486
+ setViewType("update");
487
+ }}
488
+ />
489
+ )
490
+ : undefined,
818
491
  }}
819
- organise={getUnionCollectionsConfig()}
820
- handleNavigate={handleNavigate}
821
- lib={{
822
- storage: null,
823
- ref: null,
824
- getDownloadURL: null,
825
- uploadBytes: null,
492
+ organise={{
493
+ sort: { sortId: "createdAt", type: "desc" as const },
494
+ breadcrumbType: "button",
495
+ tabAlign: "left",
826
496
  }}
827
- limit={50}
497
+ handleNavigate={handleNavigate}
498
+ limit={10}
828
499
  />
829
- </Container>
500
+ </StorySection>
501
+ );
502
+ };
503
+
504
+ // INFO: Minimal story used exclusively by e2e/dtw-crud.spec.ts. Uses a 3-field
505
+ // optional schema so DTW's required-field validation never blocks form submission.
506
+ // Excludes bareUpdateHandleComplete (which overrides API calls),
507
+ // showExport/enableExportReadPDF (which opens a HeadlessUI portal in read view),
508
+ // bareUpdateMobileView (which hides inputs), and bareReferenceDel (which triggers
509
+ // reference-check calls that can open a "references found" modal).
510
+ const DataWrapperTestSchema = [
511
+ {
512
+ id: "clientName",
513
+ name: "Client Name",
514
+ type: "string" as const,
515
+ optional: true,
516
+ },
517
+ {
518
+ id: "clientPhone",
519
+ name: "Client Phone",
520
+ type: "string" as const,
521
+ optional: true,
522
+ },
523
+ { id: "address", name: "Address", type: "string" as const, optional: true },
524
+ ];
525
+
526
+ export const DataWrapperTest = () => {
527
+ const [mount, setMount] = useState<boolean>(false);
528
+ const dataRefreshRef: any = useRef(null);
529
+ const [viewType, setViewType] = useState<"list" | "read" | "update">("list");
530
+ const [selectedId, setSelectedId] = useState<string | undefined>(undefined);
531
+ const dtwBaseUrl = "http://localhost:3000";
532
+
533
+ useEffect(() => {
534
+ setMount(true);
535
+ }, []);
536
+
537
+ useEffect(() => {
538
+ if (mount && dataRefreshRef.current) {
539
+ dataRefreshRef.current.handleRefresh();
540
+ }
541
+ }, [mount]);
542
+
543
+ const handleNavigate = (type: any, id: any) => {
544
+ setViewType(type);
545
+ setSelectedId(id);
546
+ };
547
+
548
+ if (!mount) {
549
+ return <Loading />;
550
+ }
551
+
552
+ return (
553
+ <DataTemplateWrapper
554
+ ref={dataRefreshRef}
555
+ title={"Verdant O&M Checklist Test"}
556
+ description="Minimal test story"
557
+ type={viewType}
558
+ id={selectedId}
559
+ collectionId={"verdant-om-maintenance-checklist"}
560
+ schema={DataWrapperTestSchema}
561
+ workspace={{
562
+ apiEndpointType: "mongoDB",
563
+ apiEndpoint: `${dtwBaseUrl}/api/data`,
564
+ apiEndpointToken: "",
565
+ apiUserToken: "",
566
+ assetEndpoint:
567
+ "https://firebasestorage.googleapis.com/v0/b/verdant-dev.firebasestorage.app/o/",
568
+ assetUploadEndpoint: `${dtwBaseUrl}/api/asset`,
569
+ }}
570
+ edit={{
571
+ allEditable: true,
572
+ disabledUpdate: false,
573
+ disableBatchUpdate: false,
574
+ deleteBtn: true,
575
+ }}
576
+ bareSettings={{
577
+ bareOffline: false,
578
+ bareUpdateActionComponent:
579
+ viewType === "read"
580
+ ? (data) => (
581
+ <Button
582
+ type="normal"
583
+ text="Update"
584
+ onClick={() => {
585
+ setSelectedId(data._id);
586
+ setViewType("update");
587
+ }}
588
+ />
589
+ )
590
+ : undefined,
591
+ }}
592
+ organise={{
593
+ sort: { sortId: "createdAt", type: "desc" as const },
594
+ breadcrumbType: "button",
595
+ }}
596
+ handleNavigate={handleNavigate}
597
+ limit={10}
598
+ />
830
599
  );
831
- });
600
+ };