blixify-ui-web-mcp 0.1.3 → 0.1.19

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 (168) hide show
  1. package/lib/components/action/button/README.md +12 -11
  2. package/lib/components/action/button/button.stories.tsx +38 -27
  3. package/lib/components/action/buttonGroup/buttonGroup.stories.tsx +82 -16
  4. package/lib/components/action/iconButton/README.md +1 -0
  5. package/lib/components/action/iconButton/iconButton.stories.tsx +6 -12
  6. package/lib/components/action/radioList/radioList.stories.tsx +10 -16
  7. package/lib/components/action/slider/slider.stories.tsx +9 -15
  8. package/lib/components/action/textButton/textButton.stories.tsx +6 -12
  9. package/lib/components/action/toggle/toggle.stories.tsx +6 -12
  10. package/lib/components/advanced/collapse/collapse.stories.tsx +240 -24
  11. package/lib/components/advanced/infoWindow/infoWindow.stories.tsx +10 -16
  12. package/lib/components/advanced/popUp/README.md +33 -7
  13. package/lib/components/advanced/popUp/popUp.stories.tsx +9 -15
  14. package/lib/components/auth/bareSignIn/bareSignIn.stories.tsx +36 -16
  15. package/lib/components/auth/bareSignUp/bareSignUp.stories.tsx +7 -13
  16. package/lib/components/auth/signIn/signIn.stories.tsx +4 -10
  17. package/lib/components/auth/signUp/signUp.stories.tsx +4 -10
  18. package/lib/components/chart/areaChart/areaChart.stories.tsx +12 -18
  19. package/lib/components/chart/barChart/barChart.stories.tsx +12 -18
  20. package/lib/components/chart/boxPlotChart/boxPlotChart.stories.tsx +11 -17
  21. package/lib/components/chart/bubbleChart/bubbleChart.stories.tsx +11 -17
  22. package/lib/components/chart/card/card.stories.tsx +172 -38
  23. package/lib/components/chart/ganttChart/ganttChart.stories.tsx +10 -16
  24. package/lib/components/chart/heatmapChart/heatmapChart.stories.tsx +11 -17
  25. package/lib/components/chart/lineChart/lineChart.stories.tsx +8 -14
  26. package/lib/components/chart/pieChart/pieChart.stories.tsx +11 -17
  27. package/lib/components/chart/polarAreaChart/polarAreaChart.stories.tsx +11 -17
  28. package/lib/components/chart/radarChart/radarChart.stories.tsx +6 -12
  29. package/lib/components/chart/radialBar/radialChart.stories.tsx +4 -10
  30. package/lib/components/chart/rangeBarChart/rangeBarChart.stories.tsx +8 -14
  31. package/lib/components/chart/statsCard/statsCard.stories.tsx +12 -18
  32. package/lib/components/chart/treemapChart/treemapChart.stories.tsx +11 -17
  33. package/lib/components/chat/chatAnchor/README.md +7 -0
  34. package/lib/components/chat/chatAnchor/chatAnchor.stories.tsx +15 -21
  35. package/lib/components/chat/chatBase/README.md +103 -22
  36. package/lib/components/chat/chatBase/chatBase.stories.tsx +657 -57
  37. package/lib/components/chat/chatDashboard/README.md +17 -15
  38. package/lib/components/chat/chatDashboard/chatDashboard.stories.tsx +358 -46
  39. package/lib/components/data/dataGraph/dataGraph.stories.tsx +8 -14
  40. package/lib/components/data/dataReference/dataReference.stories.tsx +9 -15
  41. package/lib/components/data/dataTemplate/README.md +443 -129
  42. package/lib/components/data/dataTemplate/dataModule.stories.tsx +10 -15
  43. package/lib/components/data/dataTemplate/dataTemplate.stories.tsx +20 -34
  44. package/lib/components/data/dataWrapper/README.md +207 -84
  45. package/lib/components/data/dataWrapper/dataWrapper.consumer.stories.tsx +2125 -0
  46. package/lib/components/data/dataWrapper/dataWrapper.edit.stories.tsx +607 -0
  47. package/lib/components/data/dataWrapper/dataWrapper.utils.stories.tsx +576 -0
  48. package/lib/components/data/dataWrapper/dataWrapper.view.stories.tsx +1273 -0
  49. package/lib/components/data/flexFilter/flexFilter.stories.tsx +171 -0
  50. package/lib/components/data/queryForm/queryForm.stories.tsx +6 -12
  51. package/lib/components/data/readQuery/readQuery.stories.tsx +6 -11
  52. package/lib/components/data/readQueryHoc/readQueryHoc.stories.tsx +6 -12
  53. package/lib/components/data/writeQuery/README.md +21 -10
  54. package/lib/components/data/writeQuery/writeQuery.stories.tsx +6 -10
  55. package/lib/components/design/actionSection/actionSection.stories.tsx +10 -14
  56. package/lib/components/design/banner/banner.stories.tsx +6 -12
  57. package/lib/components/design/bannerAction/bannerAction.stories.tsx +100 -21
  58. package/lib/components/design/blogSection/blogSection.stories.tsx +6 -12
  59. package/lib/components/design/carouselSection/carouselSection.stories.tsx +6 -12
  60. package/lib/components/design/designList/designList.stories.tsx +6 -12
  61. package/lib/components/design/faq/faq.stories.tsx +53 -31
  62. package/lib/components/design/fileGrid/fileGrid.stories.tsx +10 -16
  63. package/lib/components/design/fileViewer/fileViewer.stories.tsx +6 -12
  64. package/lib/components/design/footerSection/footerSection.stories.tsx +6 -12
  65. package/lib/components/design/googleReview/googleReview.stories.tsx +6 -12
  66. package/lib/components/design/heroSection/heroSection.stories.tsx +10 -16
  67. package/lib/components/design/iconList/iconList.stories.tsx +6 -12
  68. package/lib/components/design/imageGallery/imageGallery.stories.tsx +6 -12
  69. package/lib/components/design/imageZoom/imageZoom.stories.tsx +6 -12
  70. package/lib/components/design/leftContent/leftContent.stories.tsx +6 -12
  71. package/lib/components/design/logoCloud/logoCloud.stories.tsx +6 -12
  72. package/lib/components/design/onboarding/onboarding.stories.tsx +8 -14
  73. package/lib/components/design/pricing/pricing.stories.tsx +6 -12
  74. package/lib/components/design/rightContent/rightContent.stories.tsx +6 -12
  75. package/lib/components/design/sectionList/sectionList.stories.tsx +6 -12
  76. package/lib/components/design/teams/teams.stories.tsx +6 -12
  77. package/lib/components/display/alert/alert.stories.tsx +8 -14
  78. package/lib/components/display/calendar/README.md +49 -0
  79. package/lib/components/display/calendar/calendar.stories.tsx +78 -35
  80. package/lib/components/display/clipboard/clipboard.stories.tsx +6 -12
  81. package/lib/components/display/detailList/detailList.stories.tsx +172 -48
  82. package/lib/components/display/drawer/README.md +44 -11
  83. package/lib/components/display/drawer/drawer.stories.tsx +71 -16
  84. package/lib/components/display/emptyState/emptyState.stories.tsx +47 -15
  85. package/lib/components/display/feeds/feeds.stories.tsx +6 -12
  86. package/lib/components/display/flexTable/README.md +11 -7
  87. package/lib/components/display/flexTable/flexTable.stories.tsx +318 -20
  88. package/lib/components/display/grid/README.md +29 -30
  89. package/lib/components/display/grid/grid.stories.tsx +16 -23
  90. package/lib/components/display/kanban/kanban.stories.tsx +17 -23
  91. package/lib/components/display/list/README.md +30 -29
  92. package/lib/components/display/list/list.stories.tsx +11 -17
  93. package/lib/components/display/loading/loading.stories.tsx +7 -12
  94. package/lib/components/display/mapView/mapView.stories.tsx +9 -15
  95. package/lib/components/display/modal/modal.stories.tsx +6 -13
  96. package/lib/components/display/newCalendar/README.md +163 -0
  97. package/lib/components/display/newCalendar/newCalendar.stories.tsx +292 -0
  98. package/lib/components/display/newFeeds/README.md +53 -0
  99. package/lib/components/display/newFeeds/newFeeds.stories.tsx +104 -0
  100. package/lib/components/display/newKanban/README.md +25 -0
  101. package/lib/components/display/newKanban/newKanban.stories.tsx +131 -0
  102. package/lib/components/display/newList/README.md +30 -0
  103. package/lib/components/display/newList/newList.stories.tsx +231 -0
  104. package/lib/components/display/newPagination/README.md +15 -0
  105. package/lib/components/display/newTable/README.md +121 -0
  106. package/lib/components/display/newTable/newTable.stories.tsx +551 -0
  107. package/lib/components/display/notification/notification.stories.tsx +6 -12
  108. package/lib/components/display/progress/progress.stories.tsx +7 -12
  109. package/lib/components/display/stepper/README.md +4 -0
  110. package/lib/components/display/stepper/stepper.stories.tsx +70 -23
  111. package/lib/components/display/table/README.md +38 -37
  112. package/lib/components/display/table/table.stories.tsx +34 -36
  113. package/lib/components/display/tag/tag.stories.tsx +34 -40
  114. package/lib/components/input/address/address.stories.tsx +12 -18
  115. package/lib/components/input/canvas/canvas.stories.tsx +12 -18
  116. package/lib/components/input/checkbox/checkbox.stories.tsx +7 -13
  117. package/lib/components/input/datePicker/datePicker.stories.tsx +41 -11
  118. package/lib/components/input/form/form.stories.tsx +11 -17
  119. package/lib/components/input/markdown/README.md +80 -2
  120. package/lib/components/input/markdown/markdown.stories.tsx +83 -32
  121. package/lib/components/input/monthYearPicker/monthYearPicker.stories.tsx +52 -24
  122. package/lib/components/input/netPromoterScore/netPromoterScore.stories.tsx +6 -12
  123. package/lib/components/input/rating/rating.stories.tsx +6 -12
  124. package/lib/components/input/select/README.md +31 -29
  125. package/lib/components/input/select/select.stories.tsx +33 -39
  126. package/lib/components/input/signature/signature.stories.tsx +12 -18
  127. package/lib/components/input/textArea/textArea.stories.tsx +6 -12
  128. package/lib/components/input/textInput/textinput.stories.tsx +14 -20
  129. package/lib/components/input/uploadInput/uploadInput.stories.tsx +143 -19
  130. package/lib/components/navigation/bars/README.md +27 -0
  131. package/lib/components/navigation/bars/bars.stories.tsx +56 -19
  132. package/lib/components/navigation/bottomTab/bottomTab.stories.tsx +7 -12
  133. package/lib/components/navigation/breadcrumb/breadcrumb.stories.tsx +8 -14
  134. package/lib/components/navigation/header/header.stories.tsx +33 -21
  135. package/lib/components/navigation/leftNav/leftNav.stories.tsx +10 -16
  136. package/lib/components/navigation/tabs/tabs.stories.tsx +10 -16
  137. package/lib/components/structure/column/column.stories.tsx +10 -15
  138. package/lib/components/structure/container/container.stories.tsx +8 -13
  139. package/lib/components/structure/link/link.stories.tsx +11 -17
  140. package/lib/components/structure/text/text.stories.tsx +12 -17
  141. package/lib/components/template/authPage/authPage.stories.tsx +6 -5
  142. package/lib/components/template/calendarView/calendarView.stories.tsx +726 -0
  143. package/lib/components/template/chatDashboard/chatDashboard.stories.tsx +497 -0
  144. package/lib/components/template/landingPage/landingPage.stories.tsx +10 -7
  145. package/lib/components/template/mapLayout/mapLayout.stories.tsx +519 -0
  146. package/lib/components/template/productListing/productListing.stories.tsx +7 -13
  147. package/lib/components/template/productOverview/productOverview.stories.tsx +5 -11
  148. package/lib/components/template/shoppingCart/shoppingCart.stories.tsx +6 -12
  149. package/lib/components/template/userHomePage/userHomePage.stories.tsx +384 -524
  150. package/lib/components/template/userRankingDashboard/userRankingDashboard.stories.tsx +145 -75
  151. package/lib/mcp/tools/componentsRoot.d.ts +2 -0
  152. package/lib/mcp/tools/componentsRoot.d.ts.map +1 -0
  153. package/lib/mcp/tools/componentsRoot.js +59 -0
  154. package/lib/mcp/tools/componentsRoot.js.map +1 -0
  155. package/lib/mcp/tools/getBackendComponent.d.ts.map +1 -1
  156. package/lib/mcp/tools/getBackendComponent.js +2 -5
  157. package/lib/mcp/tools/getBackendComponent.js.map +1 -1
  158. package/lib/mcp/tools/getComponent.d.ts.map +1 -1
  159. package/lib/mcp/tools/getComponent.js +2 -5
  160. package/lib/mcp/tools/getComponent.js.map +1 -1
  161. package/lib/mcp/tools/listComponents.d.ts.map +1 -1
  162. package/lib/mcp/tools/listComponents.js +15 -10
  163. package/lib/mcp/tools/listComponents.js.map +1 -1
  164. package/lib/mcp/tools/suggestTemplate.d.ts.map +1 -1
  165. package/lib/mcp/tools/suggestTemplate.js +107 -21
  166. package/lib/mcp/tools/suggestTemplate.js.map +1 -1
  167. package/package.json +1 -1
  168. package/lib/components/data/dataWrapper/dataWrapper.stories.tsx +0 -831
@@ -74,20 +74,22 @@ const headerElement = (
74
74
 
75
75
  ##### Optional props
76
76
 
77
- | Name | Type | Default | Description |
78
- | ----------------------- | -------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------- |
79
- | `mobileSelectedChat` | `boolean` | `false` | When `true`, hides the conversation list and shows the chat panel on mobile. Set to `false` to go back to the list. |
80
- | `searchMessage` | `string` | — | Current search input value. Pass a filtered conversation to `selectedConversation` to show results. |
81
- | `headerElement` | `ReactElement` | — | Replaces the default `ChatBase` header. Use this to show the other user's name and a mobile back button. |
82
- | `bodyElement` | `ReactElement` | — | Replaces the `ChatBase` message list. Useful for showing a loading spinner while fetching messages. |
83
- | `className` | `string` | — | Tailwind classes for the conversation list panel container |
84
- | `sizeClassName` | `string` | `h-[75vh]` | Tailwind classes controlling the height/width of the conversation list panel |
85
- | `chatBaseClassName` | `string` | — | Tailwind classes for the `ChatBase` container |
86
- | `chatBaseSizeClassName` | `string` | — | Tailwind classes controlling the height/width of the `ChatBase` panel |
87
- | `onSendMessage` | `(conversation: Conversation) => void` | — | Called with the updated conversation when a message is sent |
88
- | `onChangeMessage` | `(event: any) => void` | — | Called on every keystroke in the message input |
89
- | `onSearch` | `(searchMessage: string) => void` | — | Called when the search form is submitted inside `ChatBase` |
90
- | `onChangeSearch` | `(event: any) => void` | — | Called on every keystroke in the search input inside `ChatBase` |
77
+ | Name | Type | Default | Description |
78
+ | ----------------------- | -------------------------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------- |
79
+ | `mobileSelectedChat` | `boolean` | `false` | When `true`, hides the conversation list and shows the chat panel on mobile. Set to `false` to go back to the list. |
80
+ | `disableMessageInput` | `boolean` | `false` | When `true`, message input is disabled. |
81
+ | `searchMessage` | `string` | — | Current search input value. Pass a filtered conversation to `selectedConversation` to show results. |
82
+ | `headerElement` | `ReactElement` | — | Replaces the default `ChatBase` header. Use this to show the other user's name and a mobile back button. |
83
+ | `bodyElement` | `ReactElement` | — | Replaces the `ChatBase` message list. Useful for showing a loading spinner while fetching messages. |
84
+ | `className` | `string` | — | Tailwind classes for the conversation list panel container |
85
+ | `sizeClassName` | `string` | `h-[75vh]` | Tailwind classes controlling the height/width of the conversation list panel |
86
+ | `chatBaseClassName` | `string` | — | Tailwind classes for the `ChatBase` container |
87
+ | `chatBaseSizeClassName` | `string` | — | Tailwind classes controlling the height/width of the `ChatBase` panel |
88
+ | `onSendMessage` | `(conversation: Conversation) => void` | — | Called with the updated conversation when a message is sent |
89
+ | `onChangeMessage` | `(event: any) => void` | — | Called on every keystroke in the message input |
90
+ | `onSearch` | `(searchMessage: string) => void` | — | Called when the search form is submitted inside `ChatBase` |
91
+ | `onChangeSearch` | `(event: any) => void` | — | Called on every keystroke in the search input inside `ChatBase` |
92
+ | `additionalAction` | `ChatBaseAction[]` | — | Extra items appended to the `⋮` actions menu in the chat panel. Each item requires `text` and `onClick`, with an optional `icon`. |
91
93
 
92
94
  ##### Conversation list behaviour
93
95
 
@@ -113,7 +115,7 @@ Each item in `conversationList` renders the other user's avatar and name (the me
113
115
 
114
116
  | Name | Type | Description |
115
117
  | -------------- | ------------------- | ----------------------------------------------------------------- |
116
- | `sender` | `User` | The user who sent the message |
118
+ | `sender` | `User` | The user who sent the message. |
117
119
  | `messageBody` | `string \| FileObj` | Text content or a file object for image/video messages |
118
120
  | `reactElement` | `ReactElement` | Optional — replaces `messageBody` rendering with a custom element |
119
121
  | `createdAt` | `Date` | Timestamp |
@@ -1,31 +1,41 @@
1
- import { ChevronLeftIcon } from "@heroicons/react/24/outline";
2
- import { storiesOf } from "@storybook/react";
3
- import moment from "moment";
1
+ import {
2
+ ChevronLeftIcon,
3
+ PencilSquareIcon,
4
+ TrashIcon,
5
+ } from "@heroicons/react/24/outline";
6
+ import dayjs from "../../dayjs";
4
7
  import React, { useEffect, useState } from "react";
5
- import { ChatDashboard } from ".";
8
+ import { v4 as uuidv4 } from "uuid";
9
+ import { ChatDashboard as ChatDashboardImpl } from ".";
6
10
  import { StorySection } from "../../storyUtils";
7
11
  import { IconButton } from "../../action/iconButton";
8
12
  import { Loading } from "../../display/loading";
9
- import { Conversation } from "../chatBase";
13
+ import { ChatBaseAction, Conversation } from "../chatBase";
10
14
  import { conversationList as seedConvoList, currentUser } from "../chatUtils";
11
- import README from "./README.md";
15
+ import { Button } from "../../action/button";
12
16
 
13
- const stories = storiesOf("Chat", module);
17
+ const meta = { title: "Chat/Chat Dashboard" };
18
+ export default meta;
14
19
 
15
- stories.addParameters({
16
- readme: {
17
- content: README,
18
- },
19
- });
20
20
 
21
21
  const sortByLatest = (list: Conversation[]) =>
22
22
  [...list].sort((a, b) =>
23
- moment(b.messageList[b.messageList.length - 1].createdAt).diff(
24
- moment(a.messageList[a.messageList.length - 1].createdAt)
25
- )
23
+ dayjs(b.messageList[b.messageList.length - 1].createdAt).diff(
24
+ dayjs(a.messageList[a.messageList.length - 1].createdAt),
25
+ ),
26
26
  );
27
27
 
28
- stories.add("Chat Dashboard", () => {
28
+ // INFO: demo statuses for renderConversationMeta / renderHeaderTags
29
+ const DEMO_STATUSES = ["active", "waiting", "closed", "assigned"];
30
+ const DEMO_BADGE_COLORS: Record<string, string> = {
31
+ active: "bg-green-100 text-green-700",
32
+ waiting: "bg-yellow-100 text-yellow-700",
33
+ closed: "bg-gray-100 text-gray-500",
34
+ assigned: "bg-blue-100 text-blue-700",
35
+ };
36
+
37
+ export const ChatDashboard = () => {
38
+ // INFO: Section 1 — Base chat state
29
39
  const [message, setMessage] = useState("");
30
40
  const [search, setSearch] = useState("");
31
41
  const [isLoading, setIsLoading] = useState(false);
@@ -38,11 +48,99 @@ stories.add("Chat Dashboard", () => {
38
48
  members: [],
39
49
  messageList: [],
40
50
  });
51
+ const [file, setFile] = useState<File | any>(undefined);
52
+
53
+ // INFO: Section 2 — Meta & Tags state
54
+ const [metaConvoList, setMetaConvoList] = useState<Conversation[]>([]);
55
+ const [metaConvo, setMetaConvo] = useState<Conversation>({
56
+ id: "",
57
+ members: [],
58
+ messageList: [],
59
+ });
60
+ const [metaMessage, setMetaMessage] = useState("");
61
+
62
+ // INFO: Section 3 — Pagination state
63
+ const [paginationConvoList, setPaginationConvoList] = useState<
64
+ Conversation[]
65
+ >([]);
66
+ const [paginationConvo, setPaginationConvo] = useState<Conversation>({
67
+ id: "",
68
+ members: [],
69
+ messageList: [],
70
+ });
71
+ const [paginationMessage, setPaginationMessage] = useState("");
72
+ const [page, setPage] = useState(1);
73
+ const [loadingConversations, setLoadingConversations] = useState(false);
74
+ const [loadingMessages, setLoadingMessages] = useState(false);
75
+ const [msgPage, setMsgPage] = useState(1);
76
+
77
+ // INFO: pageNum 0 generates 15 contacts to ensure the list overflows and is scrollable
78
+ const generateConversations = (pageNum: number): Conversation[] => {
79
+ const count = pageNum === 1 ? 15 : 5;
80
+ return Array.from({ length: count }, (_, i) => {
81
+ const idx = (pageNum - 1) * 5 + i;
82
+ const otherUser = {
83
+ id: `user-${idx}`,
84
+ name: `Contact ${idx + 1}`,
85
+ imageUrl: `https://ui-avatars.com/api/?name=C${idx + 1}&background=6366f1&color=fff`,
86
+ };
87
+ return {
88
+ id: uuidv4(),
89
+ members: [currentUser, otherUser],
90
+ messageList: Array.from({ length: 10 }, (_, j) => ({
91
+ sender: j % 2 === 0 ? otherUser : currentUser,
92
+ messageBody: `Message ${j + 1} from page ${pageNum}`,
93
+ createdAt: dayjs()
94
+ .subtract(10 - j, "minutes")
95
+ .toDate(),
96
+ })),
97
+ };
98
+ });
99
+ };
41
100
 
42
101
  useEffect(() => {
43
- const sorted = sortByLatest(seedConvoList);
102
+ // INFO: inject demo images of varying aspect ratios to exercise the bubble size cap
103
+ const demoImages = [
104
+ { id: 1015, w: 1600, h: 900, label: "Landscape (16:9)" },
105
+ { id: 1027, w: 900, h: 1600, label: "Portrait (9:16)" },
106
+ { id: 237, w: 1200, h: 1200, label: "Square (1:1)" },
107
+ ];
108
+ const withImages = seedConvoList.map((convo, idx) =>
109
+ idx === 0
110
+ ? {
111
+ ...convo,
112
+ messageList: [
113
+ ...convo.messageList,
114
+ ...demoImages.map((img) => ({
115
+ sender: convo.members.find((m) => m.id !== currentUser.id)!,
116
+ messageBody: {
117
+ fileName: `https://picsum.photos/id/${img.id}/${img.w}/${img.h}`,
118
+ token: img.label,
119
+ type: "image/jpeg",
120
+ },
121
+ createdAt: dayjs().subtract(demoImages.length - img.id, "minutes").toDate(),
122
+ })),
123
+ {
124
+ sender: convo.members.find((m) => m.id !== currentUser.id)!,
125
+ messageBody: {
126
+ fileName: "https://www.w3schools.com/html/mov_bbb.mp4",
127
+ token: "Sample video",
128
+ type: "video/mp4",
129
+ },
130
+ createdAt: dayjs().toDate(),
131
+ },
132
+ ],
133
+ }
134
+ : convo,
135
+ );
136
+ const sorted = sortByLatest(withImages);
44
137
  setConversationList(sorted);
45
138
  setConversation(sorted[0]);
139
+ setMetaConvoList(sorted);
140
+ setMetaConvo(sorted[0]);
141
+ const initial = generateConversations(1);
142
+ setPaginationConvoList(initial);
143
+ setPaginationConvo(initial[0]);
46
144
  }, []);
47
145
 
48
146
  const handleSelectConversation = (id: string) => {
@@ -58,10 +156,11 @@ stories.add("Chat Dashboard", () => {
58
156
  const handleSendMessage = (updated: Conversation) => {
59
157
  setConversation(updated);
60
158
  setMessage("");
61
- const updatedList = conversationList.map((c) =>
62
- c.id === updated.id ? updated : c
159
+ setConversationList(
160
+ sortByLatest(
161
+ conversationList.map((c) => (c.id === updated.id ? updated : c)),
162
+ ),
63
163
  );
64
- setConversationList(sortByLatest(updatedList));
65
164
  };
66
165
 
67
166
  const handleSearch = (searchMessage: string) => {
@@ -74,58 +173,271 @@ stories.add("Chat Dashboard", () => {
74
173
  messageList: conversation.messageList.filter(
75
174
  (m) =>
76
175
  typeof m.messageBody === "string" &&
77
- m.messageBody.toLowerCase().includes(searchMessage.toLowerCase())
176
+ m.messageBody.toLowerCase().includes(searchMessage.toLowerCase()),
78
177
  ),
79
178
  });
80
179
  }
81
180
  };
82
181
 
83
- const otherUser = conversation.members.find((m) => m.id !== currentUser.id);
84
-
85
- const headerElement = (
86
- <div className="w-full flex items-center h-12 text-white bg-primary-500">
87
- <IconButton
88
- icon={<ChevronLeftIcon className="w-6 h-6" />}
89
- background="bg-transparent"
90
- onClick={() => setMobileSelectedChat(false)}
91
- className={`${!mobileSelectedChat ? "hidden sm:flex" : ""} sm:hidden`}
92
- />
93
- <p className="ml-2 sm:ml-4 font-medium">{otherUser?.name ?? "Chat"}</p>
94
- </div>
95
- );
182
+ const handleSendUpload = (conv: Conversation, uploadedFile: File) => {
183
+ const fileUrl = URL.createObjectURL(uploadedFile);
184
+ const updated: Conversation = {
185
+ ...conv,
186
+ messageList: [
187
+ ...conv.messageList,
188
+ {
189
+ sender: currentUser,
190
+ messageBody: {
191
+ fileName: fileUrl,
192
+ token: uuidv4(),
193
+ type: uploadedFile.type,
194
+ },
195
+ createdAt: dayjs().toDate(),
196
+ },
197
+ ],
198
+ };
199
+ setConversation(updated);
200
+ setFile(undefined);
201
+ setConversationList(
202
+ sortByLatest(
203
+ conversationList.map((c) => (c.id === updated.id ? updated : c)),
204
+ ),
205
+ );
206
+ };
96
207
 
97
- const bodyElement = isLoading ? (
98
- <div className="w-full h-full flex items-center justify-center">
99
- <Loading />
100
- </div>
101
- ) : undefined;
208
+ const otherUser = conversation.members.find((m) => m.id !== currentUser.id);
102
209
 
103
210
  return (
104
211
  <>
212
+ {/* INFO: Section 1 — Base chat with file upload, search, actions */}
105
213
  <StorySection
106
- title="Chat Dashboard"
107
- description="A two-panel layout: a conversation list on the left and the active chat on the right. handleSelectedConversation is called with the conversation id when a list item is clicked. On mobile, the list and chat panels toggle via mobileSelectedChat — the header's back button sets it to false."
214
+ title="Base Chat"
215
+ description="Two-panel layout with conversation list, file upload, search, and actions. Mobile toggle via mobileSelectedChat."
108
216
  >
109
217
  <div className="w-full">
110
- <ChatDashboard
218
+ <ChatDashboardImpl
111
219
  value={message}
112
220
  searchMessage={search}
113
221
  currentUser={currentUser}
114
222
  selectedConversation={filteredConversation ?? conversation}
115
223
  conversationList={conversationList}
116
224
  mobileSelectedChat={mobileSelectedChat}
117
- sizeClassName="w-64 h-[60vh]"
225
+ sizeClassName="w-96 h-[60vh]"
118
226
  chatBaseSizeClassName="w-full h-[60vh]"
119
- headerElement={headerElement}
120
- bodyElement={bodyElement}
227
+ headerElement={
228
+ <div className="w-full flex items-center h-12 text-white bg-primary-500">
229
+ <IconButton
230
+ icon={<ChevronLeftIcon className="w-6 h-6" />}
231
+ background="bg-transparent"
232
+ onClick={() => setMobileSelectedChat(false)}
233
+ className={`${!mobileSelectedChat ? "hidden sm:flex" : ""} sm:hidden`}
234
+ />
235
+ <p className="ml-2 sm:ml-4 font-medium">
236
+ {otherUser?.name ?? "Chat"}
237
+ </p>
238
+ </div>
239
+ }
240
+ bodyElement={
241
+ isLoading ? (
242
+ <div className="w-full h-full flex items-center justify-center">
243
+ <Loading />
244
+ </div>
245
+ ) : undefined
246
+ }
247
+ fileUpload={{
248
+ allowFileUpload: true,
249
+ file,
250
+ acceptFileType: ".jpg,.jpeg,.png,.mp4,.webm",
251
+ onChangeUpload: (e) => {
252
+ if (e?.target?.files?.length > 0) setFile(e.target.files[0]);
253
+ else setFile(undefined);
254
+ },
255
+ onSendUpload: handleSendUpload,
256
+ }}
257
+ leftPanelHeaderElement={
258
+ <div className="p-4">
259
+ <Button
260
+ type="normal"
261
+ size="small"
262
+ text="New Chat"
263
+ icon={<PencilSquareIcon className="w-4 h-4" />}
264
+ onClick={() => alert("New Chat clicked")}
265
+ className="w-full"
266
+ />
267
+ </div>
268
+ }
121
269
  onChangeMessage={(e) => setMessage(e.target.value)}
122
270
  onSendMessage={handleSendMessage}
123
271
  onChangeSearch={(e) => setSearch(e.target.value)}
124
272
  onSearch={handleSearch}
125
273
  handleSelectedConversation={handleSelectConversation}
274
+ additionalAction={
275
+ [
276
+ {
277
+ text: "Clear Chat",
278
+ onClick: () =>
279
+ setConversation((prev) => ({ ...prev, messageList: [] })),
280
+ icon: <TrashIcon className="w-3 h-3" />,
281
+ },
282
+ ] as ChatBaseAction[]
283
+ }
284
+ />
285
+ </div>
286
+ </StorySection>
287
+
288
+ {/* INFO: Section 2 — renderConversationMeta + renderHeaderTags */}
289
+ <StorySection
290
+ title="Meta Badges & Header Tags"
291
+ description="renderConversationMeta shows status badges and 'Needs Reply' on left panel items. renderHeaderTags shows tags in the chat header."
292
+ >
293
+ <div className="w-full">
294
+ <ChatDashboardImpl
295
+ value={metaMessage}
296
+ currentUser={currentUser}
297
+ selectedConversation={metaConvo}
298
+ conversationList={metaConvoList}
299
+ sizeClassName="w-96 h-[60vh]"
300
+ chatBaseSizeClassName="w-full h-[60vh]"
301
+ headerElement={
302
+ <div className="w-full flex items-center h-12 px-4 text-white bg-primary-500">
303
+ <p className="font-medium">
304
+ {metaConvo.members.find((m) => m.id !== currentUser.id)
305
+ ?.name ?? "Chat"}
306
+ </p>
307
+ </div>
308
+ }
309
+ renderConversationMeta={(convo) => {
310
+ const status =
311
+ DEMO_STATUSES[
312
+ Math.abs(convo.id.charCodeAt(0)) % DEMO_STATUSES.length
313
+ ];
314
+ const needsReply = convo.id.charCodeAt(0) % 2 === 0;
315
+ return (
316
+ <div className="flex gap-1 mt-1 flex-wrap">
317
+ <span
318
+ className={`text-[10px] px-1.5 py-0.5 rounded ${DEMO_BADGE_COLORS[status]}`}
319
+ >
320
+ {status}
321
+ </span>
322
+ {needsReply && (
323
+ <span className="text-[10px] px-1.5 py-0.5 rounded bg-orange-100 text-orange-700">
324
+ Needs Reply
325
+ </span>
326
+ )}
327
+ </div>
328
+ );
329
+ }}
330
+ renderHeaderTags={(convo) => {
331
+ const status =
332
+ DEMO_STATUSES[
333
+ Math.abs(convo.id.charCodeAt(0)) % DEMO_STATUSES.length
334
+ ];
335
+ return (
336
+ <div className="flex gap-1">
337
+ <span className="text-[10px] px-1.5 py-0.5 rounded bg-white/20 text-white">
338
+ {status}
339
+ </span>
340
+ <span className="text-[10px] px-1.5 py-0.5 rounded bg-white/20 text-white">
341
+ whatsapp
342
+ </span>
343
+ </div>
344
+ );
345
+ }}
346
+ onChangeMessage={(e) => setMetaMessage(e.target.value)}
347
+ onSendMessage={(updated) => {
348
+ setMetaConvo(updated);
349
+ setMetaMessage("");
350
+ setMetaConvoList((prev) =>
351
+ sortByLatest(
352
+ prev.map((c) => (c.id === updated.id ? updated : c)),
353
+ ),
354
+ );
355
+ }}
356
+ handleSelectedConversation={(id) => {
357
+ const found = metaConvoList.find((c) => c.id === id);
358
+ if (found) setMetaConvo(found);
359
+ }}
360
+ />
361
+ </div>
362
+ </StorySection>
363
+
364
+ {/* INFO: Section 3 — Pagination scroll callbacks */}
365
+ <StorySection
366
+ title="Pagination (Infinite Scroll)"
367
+ description="onConversationListScrollEnd loads more contacts with a spinner at the bottom. onMessageListScrollTop prepends older messages with a spinner at the top. Scroll to see both effects."
368
+ >
369
+ <div className="w-full">
370
+ <ChatDashboardImpl
371
+ value={paginationMessage}
372
+ currentUser={currentUser}
373
+ selectedConversation={paginationConvo}
374
+ conversationList={paginationConvoList}
375
+ isLoadingConversations={loadingConversations}
376
+ isLoadingMessages={loadingMessages}
377
+ sizeClassName="w-96 h-[60vh]"
378
+ chatBaseSizeClassName="w-full h-[60vh]"
379
+ headerElement={
380
+ <div className="w-full flex items-center h-12 px-4 text-white bg-primary-500">
381
+ <p className="font-medium">
382
+ {paginationConvo.members.find(
383
+ (m) => m.id !== currentUser.id,
384
+ )?.name ?? "Chat"}
385
+ </p>
386
+ </div>
387
+ }
388
+ onConversationListScrollEnd={() => {
389
+ setLoadingConversations(true);
390
+ const nextPage = page + 1;
391
+ setTimeout(() => {
392
+ const more = generateConversations(nextPage);
393
+ setPaginationConvoList((prev) => [...prev, ...more]);
394
+ setPage(nextPage);
395
+ setLoadingConversations(false);
396
+ }, 1000);
397
+ }}
398
+ onMessageListScrollTop={() => {
399
+ setLoadingMessages(true);
400
+ const nextMsgPage = msgPage + 1;
401
+ setTimeout(() => {
402
+ setPaginationConvo((prev) => ({
403
+ ...prev,
404
+ messageList: [
405
+ ...Array.from({ length: 10 }, (_, i) => ({
406
+ sender:
407
+ i % 2 === 0
408
+ ? prev.members.find(
409
+ (m) => m.id !== currentUser.id,
410
+ )!
411
+ : currentUser,
412
+ messageBody: `Older message ${i + 1} from page ${nextMsgPage}`,
413
+ createdAt: dayjs()
414
+ .subtract(nextMsgPage * 10 + (10 - i), "minutes")
415
+ .toDate(),
416
+ })),
417
+ ...prev.messageList,
418
+ ],
419
+ }));
420
+ setMsgPage(nextMsgPage);
421
+ setLoadingMessages(false);
422
+ }, 1000);
423
+ }}
424
+ onChangeMessage={(e) => setPaginationMessage(e.target.value)}
425
+ onSendMessage={(updated) => {
426
+ setPaginationConvo(updated);
427
+ setPaginationMessage("");
428
+ setPaginationConvoList((prev) =>
429
+ sortByLatest(
430
+ prev.map((c) => (c.id === updated.id ? updated : c)),
431
+ ),
432
+ );
433
+ }}
434
+ handleSelectedConversation={(id) => {
435
+ const found = paginationConvoList.find((c) => c.id === id);
436
+ if (found) setPaginationConvo(found);
437
+ }}
126
438
  />
127
439
  </div>
128
440
  </StorySection>
129
441
  </>
130
442
  );
131
- });
443
+ }
@@ -1,23 +1,17 @@
1
- import { storiesOf } from "@storybook/react";
2
1
  import React from "react";
3
2
  import { StorySection } from "../../storyUtils";
4
- import README from "./README.md";
5
- import { DataGraph } from "./index";
3
+ import { DataGraph as DataGraphImpl } from "./index";
6
4
 
7
- const stories = storiesOf("Data", module);
5
+ const meta = { title: "Data/Data Graph" };
6
+ export default meta;
8
7
 
9
- stories.addParameters({
10
- readme: {
11
- content: README,
12
- },
13
- });
14
8
 
15
9
  const workspace = {
16
10
  apiEndpoint: "http://localhost:3000/api",
17
11
  apiEndpointType: "mongoDB" as const,
18
12
  };
19
13
 
20
- stories.add("Data Graph", () => {
14
+ export const DataGraph = () => {
21
15
  return (
22
16
  <>
23
17
  <StorySection
@@ -25,7 +19,7 @@ stories.add("Data Graph", () => {
25
19
  description="type='card' renders a stat card. queryBaseType='count' counts records; 'sum' totals a numeric field specified in queryAttributeId. queryBaseDateId enables a date range picker on the card."
26
20
  >
27
21
  <div className="w-full">
28
- <DataGraph
22
+ <DataGraphImpl
29
23
  charts={[
30
24
  {
31
25
  gridSize: 1,
@@ -56,10 +50,10 @@ stories.add("Data Graph", () => {
56
50
 
57
51
  <StorySection
58
52
  title="Mixed Dashboard"
59
- description="Combine multiple chart types in one DataGraph. Each chart's gridSize controls how many columns it spans within the grid."
53
+ description="Combine multiple chart types in one DataGraphImpl. Each chart's gridSize controls how many columns it spans within the grid."
60
54
  >
61
55
  <div className="w-full">
62
- <DataGraph
56
+ <DataGraphImpl
63
57
  charts={[
64
58
  {
65
59
  gridSize: 1,
@@ -100,4 +94,4 @@ stories.add("Data Graph", () => {
100
94
  </StorySection>
101
95
  </>
102
96
  );
103
- });
97
+ }
@@ -1,24 +1,18 @@
1
- import { storiesOf } from "@storybook/react";
2
1
  import axios from "axios";
3
2
  import React, { useState } from "react";
4
- import { DataReference } from "../dataReference/index";
3
+ import { DataReference as DataReferenceImpl } from "../dataReference/index";
5
4
  import { StorySection } from "../../storyUtils";
6
- import README from "./README.md";
7
5
 
8
- const stories = storiesOf("Data", module);
6
+ const meta = { title: "Data/Data Reference" };
7
+ export default meta;
9
8
 
10
- stories.addParameters({
11
- readme: {
12
- content: README,
13
- },
14
- });
15
9
 
16
10
  const devSettings = {
17
11
  apiEndpoint: "http://localhost:3000/api",
18
12
  apiEndpointType: "mondayServer" as const,
19
13
  };
20
14
 
21
- stories.add("Data Reference", () => {
15
+ export const DataReference = () => {
22
16
  const [singleValue, setSingleValue] = useState("");
23
17
  const [filteredValue, setFilteredValue] = useState("");
24
18
  const [multiValue, setMultiValue] = useState<string[]>([]);
@@ -30,7 +24,7 @@ stories.add("Data Reference", () => {
30
24
  description="type='reference' renders a single-select. Options are fetched from the referenceSpaceId collection and labelled by referenceLabel."
31
25
  >
32
26
  <div className="w-80">
33
- <DataReference
27
+ <DataReferenceImpl
34
28
  value={singleValue}
35
29
  schema={{
36
30
  id: "salesSelected",
@@ -52,7 +46,7 @@ stories.add("Data Reference", () => {
52
46
  description="referenceQuery filters the fetched options server-side. referenceSort controls the order. subLabel adds a secondary hint below the field label."
53
47
  >
54
48
  <div className="w-80">
55
- <DataReference
49
+ <DataReferenceImpl
56
50
  value={filteredValue}
57
51
  schema={{
58
52
  id: "filteredSales",
@@ -79,7 +73,7 @@ stories.add("Data Reference", () => {
79
73
  description="type='listReference' enables multi-select — value is a string array. closeOnClickOutside keeps the dropdown open until the user clicks away."
80
74
  >
81
75
  <div className="w-80">
82
- <DataReference
76
+ <DataReferenceImpl
83
77
  value={multiValue}
84
78
  schema={{
85
79
  id: "assignees",
@@ -103,7 +97,7 @@ stories.add("Data Reference", () => {
103
97
  dark
104
98
  >
105
99
  <div className="w-80">
106
- <DataReference
100
+ <DataReferenceImpl
107
101
  value={singleValue}
108
102
  schema={{
109
103
  id: "darkSales",
@@ -122,4 +116,4 @@ stories.add("Data Reference", () => {
122
116
  </StorySection>
123
117
  </>
124
118
  );
125
- });
119
+ }