@levo-so/blocks 0.1.54 → 0.1.59

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 (117) hide show
  1. package/package.json +2 -2
  2. package/src/blocks/about-us/about-us-1.schema.ts +14 -0
  3. package/src/blocks/about-us/about-us-1.tsx +4 -2
  4. package/src/blocks/about-us/about-us-2.schema.ts +48 -0
  5. package/src/blocks/about-us/about-us-2.tsx +6 -0
  6. package/src/blocks/blogs/blog-listing-1.schema.ts +10 -0
  7. package/src/blocks/blogs/blog-listing-2.schema.ts +42 -162
  8. package/src/blocks/blogs/blog-listing-2.tsx +7 -0
  9. package/src/blocks/blogs/blog-listing-3.schema.ts +9 -0
  10. package/src/blocks/blogs/blog-post-1.schema.ts +7 -1
  11. package/src/blocks/blogs/blog-post-1.tsx +1 -1
  12. package/src/blocks/blogs/blog-post-2.schema.ts +464 -28
  13. package/src/blocks/blogs/blog-post-2.tsx +6 -13
  14. package/src/blocks/blogs/blog-post-3.schema.ts +42 -0
  15. package/src/blocks/blogs/blog-post-3.tsx +4 -1
  16. package/src/blocks/calendar/calendar-1.schema.ts +537 -0
  17. package/src/blocks/calendar/calendar-1.tsx +145 -0
  18. package/src/blocks/cards/cards-10.schema.ts +52 -4
  19. package/src/blocks/cards/cards-10.tsx +13 -1
  20. package/src/blocks/cards/cards-14.schema.ts +532 -0
  21. package/src/blocks/cards/cards-14.tsx +54 -0
  22. package/src/blocks/cards/cards-15.schema.ts +277 -0
  23. package/src/blocks/cards/cards-15.tsx +40 -0
  24. package/src/blocks/cards/cards-6.schema.ts +46 -0
  25. package/src/blocks/cards/cards-6.tsx +27 -1
  26. package/src/blocks/cards/cards-7.tsx +2 -0
  27. package/src/blocks/carousel/carousel-2.schema.ts +1128 -0
  28. package/src/blocks/carousel/carousel-2.tsx +159 -0
  29. package/src/blocks/content/content-5.schema.ts +112 -6
  30. package/src/blocks/content/content-5.tsx +19 -3
  31. package/src/blocks/content/content-6.schema.ts +16 -3
  32. package/src/blocks/content/content-6.tsx +1 -0
  33. package/src/blocks/content/content-8.schema.ts +125 -0
  34. package/src/blocks/content/content-8.tsx +27 -0
  35. package/src/blocks/content/content-9.schema.ts +2729 -0
  36. package/src/blocks/content/content-9.tsx +91 -0
  37. package/src/blocks/cta/cta-2.schema.ts +46 -4
  38. package/src/blocks/cta/cta-2.tsx +9 -1
  39. package/src/blocks/embed/embed-1.schema.ts +100 -0
  40. package/src/blocks/event/ClientOnly.tsx +27 -0
  41. package/src/blocks/event/event-details.tsx +10 -3
  42. package/src/blocks/event/event-listing-3.tsx +9 -2
  43. package/src/blocks/event/event-listing-4.tsx +11 -2
  44. package/src/blocks/event/event-listing.tsx +11 -4
  45. package/src/blocks/faq/faq-1.schema.ts +58 -1
  46. package/src/blocks/faq/faq-1.tsx +18 -1
  47. package/src/blocks/faq/faq-2.tsx +2 -1
  48. package/src/blocks/faq/faq-3.schema.ts +6 -1
  49. package/src/blocks/faq/faq-3.tsx +1 -0
  50. package/src/blocks/features/features-1/features-1.schema.ts +2 -0
  51. package/src/blocks/features/features-1/variants/index.ts +2 -0
  52. package/src/blocks/features/features-1/variants/stylized-cards-v3.ts +451 -0
  53. package/src/blocks/features/features-4.schema.ts +358 -20
  54. package/src/blocks/features/features-4.tsx +3 -1
  55. package/src/blocks/features/features-6.tsx +2 -0
  56. package/src/blocks/filter-listing/filter-listing-1.schema.ts +614 -0
  57. package/src/blocks/filter-listing/filter-listing-1.tsx +128 -0
  58. package/src/blocks/footer/footer-6.schema.ts +2 -2
  59. package/src/blocks/hero/hero-11.schema.ts +849 -0
  60. package/src/blocks/hero/hero-11.tsx +59 -0
  61. package/src/blocks/hero/hero-12.schema.ts +712 -0
  62. package/src/blocks/hero/hero-12.tsx +76 -0
  63. package/src/blocks/hero/hero-13.schema.ts +238 -0
  64. package/src/blocks/hero/hero-13.tsx +31 -0
  65. package/src/blocks/hero/hero-4.tsx +2 -0
  66. package/src/blocks/hero/hero-5.tsx +2 -0
  67. package/src/blocks/hero/hero-7.tsx +2 -1
  68. package/src/blocks/hero/hero-8.tsx +2 -0
  69. package/src/blocks/hero/hero-9.schema.ts +5 -0
  70. package/src/blocks/hero/hero-9.tsx +2 -0
  71. package/src/blocks/index.ts +27 -0
  72. package/src/blocks/navbar/navbar-1.schema.ts +6 -6
  73. package/src/blocks/navbar/navbar-1.tsx +6 -7
  74. package/src/blocks/navbar/navbar-2.schema.ts +9 -9
  75. package/src/blocks/navbar/navbar-2.tsx +7 -8
  76. package/src/blocks/navbar/navbar-3.schema.ts +9 -9
  77. package/src/blocks/navbar/navbar-3.tsx +139 -114
  78. package/src/blocks/polling/polling-1.schema.ts +134 -0
  79. package/src/blocks/polling/polling-1.tsx +466 -0
  80. package/src/blocks/products/products-listing-2.schema.ts +596 -0
  81. package/src/blocks/products/products-listing-2.tsx +56 -0
  82. package/src/blocks/products/products-listing-3.schema.ts +818 -0
  83. package/src/blocks/products/products-listing-3.tsx +125 -0
  84. package/src/blocks/profile/profile-2.schema.ts +505 -0
  85. package/src/blocks/profile/profile-2.tsx +64 -0
  86. package/src/blocks/profile/profile-3.schema.ts +867 -0
  87. package/src/blocks/profile/profile-3.tsx +98 -0
  88. package/src/blocks/services/service-1.schema.ts +1743 -0
  89. package/src/blocks/services/service-1.tsx +139 -0
  90. package/src/blocks/services/service-2.schema.ts +433 -0
  91. package/src/blocks/services/service-2.tsx +98 -0
  92. package/src/blocks/services/service-3.schema.ts +631 -0
  93. package/src/blocks/services/service-3.tsx +135 -0
  94. package/src/blocks/tabs/tab-1.schema.ts +831 -0
  95. package/src/blocks/tabs/tab-1.tsx +126 -0
  96. package/src/blocks/tabs/tab-2.schema.ts +767 -0
  97. package/src/blocks/tabs/tab-2.tsx +60 -0
  98. package/src/blocks/team/team-3.schema.ts +5 -1
  99. package/src/blocks/team/team-3.tsx +7 -0
  100. package/src/blocks/team/team-4.schema.ts +17 -13
  101. package/src/blocks/team/team-4.tsx +4 -1
  102. package/src/blocks/team/team-5.schema.ts +369 -0
  103. package/src/blocks/team/team-5.tsx +38 -0
  104. package/src/blocks/testimonial/testimonial-1.tsx +0 -1
  105. package/src/blocks/testimonial/testimonial-2.tsx +2 -1
  106. package/src/blocks/testimonial/testimonial-3.tsx +2 -0
  107. package/src/blocks/testimonial/testimonial-4.tsx +2 -1
  108. package/src/blocks/testimonial/testimonial-5.tsx +2 -0
  109. package/src/blocks/testimonial/testimonial-6.tsx +2 -1
  110. package/src/blocks/testimonial/testimonial-7.schema.ts +438 -0
  111. package/src/blocks/testimonial/testimonial-7.tsx +125 -0
  112. package/src/blocks/timeline/timeline-1.schema.ts +741 -0
  113. package/src/blocks/timeline/timeline-1.tsx +52 -0
  114. package/src/blocks/users/edit-profile.schema.ts +294 -0
  115. package/src/blocks/users/edit-profile.tsx +184 -0
  116. package/src/schemas/blocks.ts +64 -12
  117. package/src/schemas/categories.ts +36 -0
@@ -0,0 +1,134 @@
1
+ import type { IBlock } from "@levo-so/studio";
2
+
3
+ const DEFAULT_CONTENT = {
4
+ layout: null,
5
+ container: null,
6
+ header: null,
7
+ title: "Polls",
8
+ description:
9
+ "Our polls help you to get the insights you need to make informed decisions.",
10
+ polls: null,
11
+ loading_container: null,
12
+ };
13
+
14
+ export type IPolling1Content = typeof DEFAULT_CONTENT;
15
+
16
+ export const Polling1: IBlock = {
17
+ category_id: "polling",
18
+ title: "Polling 1",
19
+ key: "polling-1",
20
+ version: "v1",
21
+ content_schema: [
22
+ {
23
+ key: "layout",
24
+ label: "Layout",
25
+ field_interface: "LayoutWidget",
26
+ },
27
+ {
28
+ key: "container",
29
+ label: "Container",
30
+ field_interface: "LayoutWidget",
31
+ },
32
+ {
33
+ key: "header",
34
+ label: "Header",
35
+ field_interface: "BoxWidget",
36
+ },
37
+ {
38
+ key: "title",
39
+ label: "Title",
40
+ field_interface: "HeadingWidget",
41
+ },
42
+ {
43
+ key: "description",
44
+ label: "Description",
45
+ field_interface: "TypographyWidget",
46
+ },
47
+ {
48
+ key: "polls",
49
+ label: "Polls",
50
+ field_interface: "BoxWidget",
51
+ },
52
+ {
53
+ key: "loading_container",
54
+ label: "Loading Container",
55
+ field_interface: "BoxWidget",
56
+ },
57
+ ],
58
+ layouts: [
59
+ {
60
+ key: "default",
61
+ title: "Default",
62
+ styles: {
63
+ layout: {
64
+ width: "100%",
65
+ "padding-top": "10xl",
66
+ "padding-bottom": "10xl",
67
+ "padding-right": "8xl",
68
+ "padding-left": "8xl",
69
+ tablet: {
70
+ "padding-top": "5xl",
71
+ "padding-bottom": "5xl",
72
+ "padding-right": "5xl",
73
+ "padding-left": "5xl",
74
+ },
75
+ mobile: {
76
+ "padding-top": "2xl",
77
+ "padding-bottom": "2xl",
78
+ "padding-right": "2xl",
79
+ "padding-left": "2xl",
80
+ },
81
+ },
82
+ container: {
83
+ width: "100%",
84
+ display: "flex",
85
+ "flex-direction": "column",
86
+ "row-gap": "xl",
87
+ tablet: {
88
+ "row-gap": "3xl",
89
+ },
90
+ mobile: {
91
+ "row-gap": "4xl",
92
+ },
93
+ },
94
+ header: {
95
+ display: "flex",
96
+ "flex-direction": "column",
97
+ width: "100%",
98
+ "row-gap": "none",
99
+ },
100
+ title: {},
101
+ description: {},
102
+ polls: {
103
+ display: "flex",
104
+ "flex-direction": "column",
105
+ width: "100%",
106
+ "row-gap": "xl",
107
+ },
108
+ loading_container: {
109
+ display: "flex",
110
+ "flex-direction": "column",
111
+ "justify-content": "center",
112
+ "align-items": "center",
113
+ "min-height": "256px",
114
+ },
115
+ },
116
+ content: DEFAULT_CONTENT,
117
+ config: {
118
+ title: {
119
+ heading: {
120
+ level: "2",
121
+ },
122
+ selectedVariants: {
123
+ Heading_Level: "H2",
124
+ },
125
+ },
126
+ description: {
127
+ selectedVariants: {
128
+ Typography_Variants: "Body",
129
+ },
130
+ },
131
+ },
132
+ },
133
+ ],
134
+ };
@@ -0,0 +1,466 @@
1
+ import {
2
+ type ILevoBlockBaseProps,
3
+ Heading,
4
+ Typography,
5
+ Container,
6
+ Section,
7
+ Box,
8
+ useBlockContext,
9
+ useAuth,
10
+ } from "@levo-so/studio";
11
+ import { useCallback, useEffect, useMemo, useState } from "react";
12
+ import { cn, errorToast, Spinner, successToast, useLevo } from "@levo-so/react";
13
+ import { CircleCheckBig, LoaderCircle } from "lucide-react";
14
+
15
+ import type { IPolling1Content } from "./polling-1.schema";
16
+
17
+ const POLLS_RESPONSE_SAMPLE = {
18
+ content: {
19
+ data: [
20
+ {
21
+ _id: "7388820366332899169",
22
+ comments_count: 0,
23
+ content: "Help us prioritize our development roadmap...",
24
+ cover_image: "",
25
+ created_at: "2025-10-28T06:14:12.977Z",
26
+ created_by: "URB3BU",
27
+ expires_at: "2026-12-31T23:59:59.000Z",
28
+ kind: "poll",
29
+ likes_count: 0,
30
+ media: [],
31
+ published_at: "2025-10-28T06:14:12.977Z",
32
+ show_participant_list: true,
33
+ show_participant_vote: false,
34
+ slug: "what-feature-would-you-like-to-see-next",
35
+ title: "What feature would you like to see next?",
36
+ updated_at: "2025-10-28T06:14:12.977Z",
37
+ vote_count: 0,
38
+ workspace_id: "WZZKMUBH",
39
+ options: [
40
+ {
41
+ _id: "7388820367269693206",
42
+ post: "7388820366332899169",
43
+ label: "Collaboration Support",
44
+ count: 4,
45
+ created_at: null,
46
+ created_by: "URB3BU",
47
+ },
48
+ {
49
+ _id: "7388820367270497657",
50
+ post: "7388820366332899169",
51
+ label: "Roles based Access",
52
+ count: 8,
53
+ created_at: null,
54
+ created_by: "URB3BU",
55
+ },
56
+ {
57
+ _id: "73888203672704976",
58
+ post: "7388820366332899169",
59
+ label: "Email Gating",
60
+ count: 2,
61
+ created_at: null,
62
+ created_by: "URB3BU",
63
+ },
64
+ {
65
+ _id: "738882036727049",
66
+ post: "7388820366332899169",
67
+ label: "In Platform Image Editing",
68
+ count: 7,
69
+ created_at: null,
70
+ created_by: "URB3BU",
71
+ },
72
+ ],
73
+ total_votes: 21,
74
+ is_expired: false,
75
+ user_voted_option: "7388820367269693206",
76
+ can_vote: true,
77
+ },
78
+ {
79
+ _id: "7388820366332899169",
80
+ comments_count: 0,
81
+ content: "Share your perspective with us",
82
+ cover_image: "",
83
+ created_at: "2025-10-28T06:14:12.977Z",
84
+ created_by: "URB3BU",
85
+ expires_at: "2026-12-31T23:59:59.000Z",
86
+ kind: "poll",
87
+ likes_count: 0,
88
+ media: [],
89
+ published_at: "2025-10-28T06:14:12.977Z",
90
+ show_participant_list: true,
91
+ show_participant_vote: false,
92
+ slug: "what-do-you-look-for-in-a-good-ai-platform",
93
+ title: "What do you look for in a good AI platform?",
94
+ updated_at: "2025-10-28T06:14:12.977Z",
95
+ vote_count: 0,
96
+ workspace_id: "WZZKMUBH",
97
+ options: [
98
+ {
99
+ _id: "7388820367270497657",
100
+ post: "7388820366332899169",
101
+ label: "Good Customer Support",
102
+ count: 3,
103
+ created_at: null,
104
+ created_by: "URB3BU",
105
+ },
106
+ {
107
+ _id: "738882069693206",
108
+ post: "7388820366332899169",
109
+ label: "Rich Functionality",
110
+ count: 5,
111
+ created_at: null,
112
+ created_by: "URB3BU",
113
+ },
114
+ {
115
+ _id: "73888206969320645767",
116
+ post: "7388820366332899169",
117
+ label: "Simple but efficient",
118
+ count: 8,
119
+ created_at: null,
120
+ created_by: "URB3BU",
121
+ },
122
+ {
123
+ _id: "738882069696465754506",
124
+ post: "7388820366332899169",
125
+ label: "Good Documentation",
126
+ count: 3,
127
+ created_at: null,
128
+ created_by: "URB3BU",
129
+ },
130
+ {
131
+ _id: "7388820696964657897869766",
132
+ post: "7388820366332899169",
133
+ label: "Able to DIY",
134
+ count: 10,
135
+ created_at: null,
136
+ created_by: "URB3BU",
137
+ },
138
+ ],
139
+ total_votes: 29,
140
+ is_expired: false,
141
+ user_voted_option: "73888206969320645767",
142
+ can_vote: true,
143
+ },
144
+ ],
145
+ meta: {
146
+ pages: 1,
147
+ total: 2,
148
+ },
149
+ },
150
+ status: true,
151
+ };
152
+
153
+ const PollOption: React.FC<{
154
+ option: (typeof POLLS_RESPONSE_SAMPLE.content.data)[0]["options"][number];
155
+ totalVotes: number;
156
+ selectedOption: string | null;
157
+ pollId: string;
158
+ fetchPolls?: () => Promise<void>;
159
+ }> = ({ option, totalVotes, selectedOption, pollId, fetchPolls }) => {
160
+ const levoClient = useLevo();
161
+ const { isBuilder } = useBlockContext();
162
+
163
+ const [isUserOnOption, setIsUserOnOption] = useState(false);
164
+ const [isVoting, setIsVoting] = useState(false);
165
+
166
+ const [width, setWidth] = useState("0%");
167
+
168
+ const isCurrentOptionSelected = selectedOption === option._id;
169
+
170
+ const percentage = useMemo(() => {
171
+ if (
172
+ Number.isFinite(totalVotes) &&
173
+ Number.isFinite(option?.count) &&
174
+ totalVotes > 0
175
+ ) {
176
+ return ((option.count / totalVotes) * 100)?.toFixed(0);
177
+ }
178
+ return null;
179
+ }, [totalVotes, option?.count]);
180
+
181
+ useEffect(() => {
182
+ if (percentage !== null) {
183
+ setTimeout(() => {
184
+ setWidth(`${percentage}%`);
185
+ }, 500);
186
+ }
187
+ }, [percentage]);
188
+
189
+ const handleOptionClick = async () => {
190
+ if (isBuilder || isVoting || isCurrentOptionSelected) return;
191
+
192
+ try {
193
+ setIsVoting(true);
194
+ const response = await levoClient?.fetch
195
+ .url(`/v1/community/forum/${pollId}/register-vote`)
196
+ .post({
197
+ option: option?._id,
198
+ })
199
+ ?.json<{
200
+ content: {
201
+ data: {
202
+ voted: boolean;
203
+ option: string;
204
+ total_votes: number;
205
+ message: string;
206
+ };
207
+ };
208
+ status: boolean;
209
+ }>();
210
+
211
+ if (
212
+ response?.status &&
213
+ response?.content?.data &&
214
+ response?.content?.data?.voted
215
+ ) {
216
+ successToast(response?.content?.data?.message);
217
+ fetchPolls?.();
218
+ }
219
+ } catch (err: unknown) {
220
+ errorToast("Something went wrong. Please try again");
221
+ } finally {
222
+ setIsVoting(false);
223
+ }
224
+ };
225
+
226
+ return (
227
+ <div
228
+ onMouseEnter={() => {
229
+ setIsUserOnOption(true);
230
+ }}
231
+ onMouseLeave={() => {
232
+ setIsUserOnOption(false);
233
+ }}
234
+ onClick={handleOptionClick}
235
+ className="hover:cursor-pointer"
236
+ >
237
+ <div className="flex items-center justify-between">
238
+ <Heading
239
+ elementKey="poll-option-title"
240
+ style={{
241
+ fontSize: "16px",
242
+ lineHeight: "140%",
243
+ fontWeight: "500",
244
+ }}
245
+ >
246
+ {option.label}
247
+ </Heading>
248
+
249
+ {(isUserOnOption || isCurrentOptionSelected) && isVoting === false && (
250
+ <CircleCheckBig className="text-brand size-5" />
251
+ )}
252
+ </div>
253
+ <div className="flex items-center justify-between gap-x-2">
254
+ <div
255
+ className={cn(
256
+ "relative h-2 w-full rounded",
257
+ isVoting && "animate-pulse",
258
+ )}
259
+ style={{
260
+ maxWidth: "95%",
261
+ backgroundColor: "oklch(0.9416 0.0121 259.8229)",
262
+ }}
263
+ >
264
+ <div
265
+ className="bg-brand absolute top-0 right-0 left-0 h-2 rounded transition-all ease-out"
266
+ style={{
267
+ width,
268
+ transformOrigin: "left center",
269
+ transitionDuration: "2000ms",
270
+ }}
271
+ />
272
+ </div>
273
+ <div>
274
+ {isVoting ? (
275
+ <LoaderCircle
276
+ className={cn("text-brand size-6 animate-spin font-bold")}
277
+ style={{
278
+ height: "25.59px",
279
+ }}
280
+ />
281
+ ) : (
282
+ <Typography
283
+ elementKey="poll-option-votes-percentage-text"
284
+ style={{
285
+ fontSize: "16px",
286
+ lineHeight: "1.6",
287
+ }}
288
+ >
289
+ {percentage ?? 0}%
290
+ </Typography>
291
+ )}
292
+ </div>
293
+ </div>
294
+ </div>
295
+ );
296
+ };
297
+
298
+ const Poll: React.FC<{
299
+ poll: (typeof POLLS_RESPONSE_SAMPLE.content.data)[0];
300
+ fetchPolls?: () => Promise<void>;
301
+ isLoading?: boolean;
302
+ }> = ({ poll, fetchPolls, isLoading }) => {
303
+ return (
304
+ <div className="flex flex-col gap-y-4">
305
+ <div className="flex flex-col-reverse">
306
+ <Heading
307
+ elementKey="poll-title"
308
+ style={{
309
+ fontWeight: "600",
310
+ fontSize: "18px",
311
+ lineHeight: "140%",
312
+ }}
313
+ >
314
+ {poll.title}
315
+ </Heading>
316
+ <Typography
317
+ elementKey="poll-description"
318
+ style={{
319
+ fontSize: "14px",
320
+ lineHeight: "1.5",
321
+ }}
322
+ >
323
+ {poll.content}
324
+ </Typography>
325
+ </div>
326
+ <div
327
+ className={cn("relative rounded-md p-3", isLoading && "animate-pulse")}
328
+ style={{
329
+ backgroundColor: "oklch(0.9758 0.0057 264.5326)",
330
+ }}
331
+ >
332
+ {isLoading && (
333
+ <div
334
+ className="absolute inset-0 z-50 flex animate-pulse cursor-wait items-center justify-center rounded-md"
335
+ style={{
336
+ backgroundColor: "rgba(255, 255, 255, 0.4)",
337
+ }}
338
+ />
339
+ )}
340
+ {poll?.options?.map((v) => {
341
+ return (
342
+ <PollOption
343
+ option={v}
344
+ totalVotes={poll.total_votes}
345
+ key={v._id}
346
+ selectedOption={poll?.user_voted_option}
347
+ pollId={poll._id}
348
+ fetchPolls={fetchPolls}
349
+ />
350
+ );
351
+ })}
352
+ </div>
353
+ <div className="flex w-full items-center justify-between">
354
+ <div>
355
+ <Typography
356
+ elementKey="poll-votes-text"
357
+ style={{
358
+ fontSize: "18px",
359
+ lineHeight: "1.5",
360
+ fontWeight: "500",
361
+ }}
362
+ >
363
+ {poll?.total_votes} {poll?.total_votes === 1 ? "vote" : "votes"}
364
+ </Typography>
365
+ </div>
366
+ <Typography
367
+ elementKey="poll-helper-text"
368
+ style={{
369
+ fontSize: "12px",
370
+ lineHeight: "1.5",
371
+ }}
372
+ >
373
+ *Voting in this poll no longer yields commission.
374
+ </Typography>
375
+ </div>
376
+ </div>
377
+ );
378
+ };
379
+
380
+ const Polling1: React.FC<ILevoBlockBaseProps<IPolling1Content>> = ({
381
+ content,
382
+ }) => {
383
+ const levoClient = useLevo();
384
+ const { isBuilder } = useBlockContext();
385
+
386
+ const { account, isAuthLoaded } = useAuth();
387
+
388
+ const [isLoading, setIsLoading] = useState(false);
389
+ const [isLoaded, setIsLoaded] = useState(false);
390
+ const [pollList, setPollList] = useState<any[]>([]);
391
+
392
+ const fetchPolls = useCallback(async () => {
393
+ setIsLoading(true);
394
+ try {
395
+ const response = await levoClient.fetch
396
+ .get("/v1/community/forum/list")
397
+ .json<typeof POLLS_RESPONSE_SAMPLE>();
398
+
399
+ if (response && response?.content?.data) {
400
+ setPollList(response?.content?.data);
401
+ setIsLoaded(true);
402
+ }
403
+ } catch (err: unknown) {
404
+ console.error(err);
405
+ } finally {
406
+ setIsLoading(false);
407
+ }
408
+ }, []);
409
+
410
+ useEffect(() => {
411
+ if (isBuilder) return;
412
+ fetchPolls();
413
+ }, []);
414
+
415
+ return (
416
+ <Section elementKey="layout">
417
+ <Container elementKey="container">
418
+ <Box elementKey="header">
419
+ <Heading elementKey="title" />
420
+ <Typography elementKey="description" />
421
+ </Box>
422
+ <Box elementKey="polls">
423
+ {isBuilder ? (
424
+ <>
425
+ {POLLS_RESPONSE_SAMPLE?.content?.data?.map(
426
+ (item: any, index: number) => {
427
+ return (
428
+ <Poll
429
+ poll={item}
430
+ isLoading={isLoading}
431
+ key={`polls.${index}`}
432
+ fetchPolls={fetchPolls}
433
+ />
434
+ );
435
+ },
436
+ )}
437
+ </>
438
+ ) : (
439
+ <>
440
+ {isAuthLoaded && account && isLoaded ? (
441
+ <>
442
+ {pollList?.map((item: any, index: number) => {
443
+ return (
444
+ <Poll
445
+ poll={item}
446
+ isLoading={isLoading}
447
+ key={`polls.${index}`}
448
+ fetchPolls={fetchPolls}
449
+ />
450
+ );
451
+ })}
452
+ </>
453
+ ) : (
454
+ <Box elementKey="loading_container">
455
+ <Spinner className="text-brand" />
456
+ </Box>
457
+ )}
458
+ </>
459
+ )}
460
+ </Box>
461
+ </Container>
462
+ </Section>
463
+ );
464
+ };
465
+
466
+ export default Polling1;