@levo-so/blocks 0.1.101 → 0.1.102

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 (159) hide show
  1. package/package.json +6 -5
  2. package/src/blocks/about-us/about-us-1.schema.ts +302 -295
  3. package/src/blocks/about-us/about-us-2.schema.ts +225 -220
  4. package/src/blocks/blogs/blog-author-1.schema.ts +719 -0
  5. package/src/blocks/blogs/blog-author-1.tsx +121 -0
  6. package/src/blocks/blogs/blog-listing-1.tsx +111 -33
  7. package/src/blocks/blogs/blog-listing-2.schema.ts +15 -33
  8. package/src/blocks/blogs/blog-listing-2.tsx +118 -33
  9. package/src/blocks/blogs/blog-listing-3.schema.ts +10 -22
  10. package/src/blocks/blogs/blog-listing-3.tsx +118 -33
  11. package/src/blocks/blogs/blog-post-1.schema.ts +766 -16
  12. package/src/blocks/blogs/blog-post-1.tsx +256 -32
  13. package/src/blocks/calendar/calendar-1.schema.ts +641 -192
  14. package/src/blocks/calendar/calendar-1.tsx +1 -0
  15. package/src/blocks/cards/cards-10.schema.ts +557 -544
  16. package/src/blocks/cards/cards-11.schema.ts +525 -419
  17. package/src/blocks/cards/cards-12.schema.ts +538 -147
  18. package/src/blocks/cards/cards-13.schema.ts +482 -312
  19. package/src/blocks/cards/cards-14.schema.ts +423 -196
  20. package/src/blocks/cards/cards-15.schema.ts +309 -143
  21. package/src/blocks/cards/cards-16.schema.ts +1277 -175
  22. package/src/blocks/cards/cards-17.schema.ts +2908 -1303
  23. package/src/blocks/cards/cards-18.schema.ts +1404 -217
  24. package/src/blocks/cards/cards-19.schema.ts +1105 -1084
  25. package/src/blocks/cards/cards-19.tsx +4 -5
  26. package/src/blocks/cards/cards-2.schema.ts +513 -42
  27. package/src/blocks/cards/cards-20.schema.ts +790 -212
  28. package/src/blocks/cards/cards-21.schema.ts +1215 -229
  29. package/src/blocks/cards/cards-21.tsx +51 -6
  30. package/src/blocks/cards/cards-22.schema.ts +1252 -2809
  31. package/src/blocks/cards/cards-22.tsx +6 -7
  32. package/src/blocks/cards/cards-23.schema.ts +571 -109
  33. package/src/blocks/cards/cards-3.schema.ts +355 -116
  34. package/src/blocks/cards/cards-4.schema.ts +805 -321
  35. package/src/blocks/cards/cards-5.schema.ts +216 -64
  36. package/src/blocks/cards/cards-6.schema.ts +568 -1330
  37. package/src/blocks/cards/cards-7.schema.ts +1181 -1378
  38. package/src/blocks/cards/cards-8.schema.ts +585 -221
  39. package/src/blocks/cards/cards-9.schema.ts +763 -382
  40. package/src/blocks/carousel/carousel-1.schema.ts +257 -502
  41. package/src/blocks/carousel/carousel-2.schema.ts +771 -761
  42. package/src/blocks/community/community-1.schema.ts +8 -23
  43. package/src/blocks/community/community-1.tsx +3 -3
  44. package/src/blocks/community/community-post-1.tsx +4 -4
  45. package/src/blocks/contact-us/contact-us-1.schema.ts +360 -14
  46. package/src/blocks/contact-us/contact-us-2.schema.ts +441 -325
  47. package/src/blocks/content/content-1.schema.ts +800 -381
  48. package/src/blocks/content/content-10.schema.ts +763 -20
  49. package/src/blocks/content/content-2.schema.ts +426 -52
  50. package/src/blocks/content/content-3.schema.ts +631 -432
  51. package/src/blocks/content/content-4.schema.ts +378 -123
  52. package/src/blocks/content/content-5.schema.ts +1152 -682
  53. package/src/blocks/content/content-6.schema.ts +679 -707
  54. package/src/blocks/content/content-7.schema.ts +694 -291
  55. package/src/blocks/content/content-8.schema.ts +247 -7
  56. package/src/blocks/content/content-9.schema.ts +855 -116
  57. package/src/blocks/cta/cta-1.schema.ts +294 -62
  58. package/src/blocks/cta/cta-2.schema.ts +302 -66
  59. package/src/blocks/cta/cta-2.tsx +1 -2
  60. package/src/blocks/cta/cta-3.schema.ts +312 -62
  61. package/src/blocks/embed/embed-1.schema.ts +181 -0
  62. package/src/blocks/event/event-listing-2.schema.ts +5 -11
  63. package/src/blocks/event/event-listing-3.schema.ts +5 -11
  64. package/src/blocks/event/event-listing-4.schema.ts +5 -11
  65. package/src/blocks/event/event-listing.schema.ts +5 -11
  66. package/src/blocks/faq/faq-1.schema.ts +439 -260
  67. package/src/blocks/faq/faq-2.schema.ts +444 -266
  68. package/src/blocks/faq/faq-3.schema.ts +1420 -224
  69. package/src/blocks/features/features-1/features-1.schema.ts +21 -21
  70. package/src/blocks/features/features-1/variants/default.ts +120 -138
  71. package/src/blocks/features/features-1/variants/index.ts +2 -2
  72. package/src/blocks/features/features-1/variants/minimal-list.ts +139 -0
  73. package/src/blocks/features/features-1/variants/stylized-cards-v1.ts +69 -385
  74. package/src/blocks/features/features-1/variants/stylized-cards-v2.ts +74 -408
  75. package/src/blocks/features/features-2.schema.ts +368 -286
  76. package/src/blocks/features/features-3.schema.ts +557 -280
  77. package/src/blocks/features/features-4.schema.ts +940 -432
  78. package/src/blocks/features/features-5.schema.ts +566 -407
  79. package/src/blocks/features/features-6.schema.ts +557 -523
  80. package/src/blocks/filter-listing/filter-listing-1.schema.ts +922 -270
  81. package/src/blocks/floating-buttons/floating-button-1.schema.ts +123 -0
  82. package/src/blocks/footer/footer-1.schema.ts +383 -401
  83. package/src/blocks/footer/footer-2.schema.ts +337 -878
  84. package/src/blocks/footer/footer-3.schema.ts +445 -381
  85. package/src/blocks/footer/footer-4.schema.ts +610 -439
  86. package/src/blocks/footer/footer-5.schema.ts +367 -321
  87. package/src/blocks/footer/footer-6.schema.ts +610 -357
  88. package/src/blocks/form/form-1.schema.ts +237 -0
  89. package/src/blocks/hero/hero-1.schema.ts +251 -221
  90. package/src/blocks/hero/hero-10.schema.ts +23 -14
  91. package/src/blocks/hero/hero-11.schema.ts +728 -601
  92. package/src/blocks/hero/hero-12.schema.ts +524 -334
  93. package/src/blocks/hero/hero-13.schema.ts +83 -95
  94. package/src/blocks/hero/hero-2.schema.ts +277 -68
  95. package/src/blocks/hero/hero-3.schema.ts +371 -235
  96. package/src/blocks/hero/hero-3.tsx +1 -1
  97. package/src/blocks/hero/hero-4.schema.ts +605 -338
  98. package/src/blocks/hero/hero-5.schema.ts +545 -61
  99. package/src/blocks/hero/hero-6.schema.ts +446 -257
  100. package/src/blocks/hero/hero-6.tsx +1 -1
  101. package/src/blocks/hero/hero-7.schema.ts +1299 -131
  102. package/src/blocks/hero/hero-7.tsx +43 -23
  103. package/src/blocks/hero/hero-8.schema.ts +736 -236
  104. package/src/blocks/hero/hero-9.schema.ts +908 -58
  105. package/src/blocks/index.ts +2 -0
  106. package/src/blocks/jobs/jobs-1.schema.ts +741 -219
  107. package/src/blocks/logos/logos-1.schema.ts +317 -321
  108. package/src/blocks/logos/logos-1.tsx +32 -19
  109. package/src/blocks/navbar/navbar-1.schema.ts +319 -186
  110. package/src/blocks/navbar/navbar-2.schema.ts +311 -190
  111. package/src/blocks/navbar/navbar-3.schema.ts +612 -570
  112. package/src/blocks/navbar/navbar-4.schema.ts +741 -1250
  113. package/src/blocks/polling/polling-1.schema.ts +161 -0
  114. package/src/blocks/pricing/pricing-1.schema.ts +549 -626
  115. package/src/blocks/products/products-listing-1.schema.ts +363 -249
  116. package/src/blocks/products/products-listing-2.schema.ts +515 -257
  117. package/src/blocks/products/products-listing-3.schema.ts +789 -836
  118. package/src/blocks/profile/profile-1.schema.ts +168 -187
  119. package/src/blocks/profile/profile-2.schema.ts +150 -122
  120. package/src/blocks/profile/profile-3.schema.ts +307 -256
  121. package/src/blocks/profile/profile-4.schema.ts +372 -504
  122. package/src/blocks/profile/profile-5.schema.ts +186 -79
  123. package/src/blocks/services/service-1.schema.ts +700 -631
  124. package/src/blocks/services/service-2.schema.ts +559 -82
  125. package/src/blocks/services/service-2.tsx +5 -3
  126. package/src/blocks/services/service-3.schema.ts +587 -215
  127. package/src/blocks/services/service-3.tsx +4 -2
  128. package/src/blocks/stats/stats-1.schema.ts +347 -257
  129. package/src/blocks/stats/stats-2.schema.ts +14 -2
  130. package/src/blocks/stats/variants/default.ts +14 -37
  131. package/src/blocks/stats/variants/extra.ts +232 -0
  132. package/src/blocks/stats/variants/hero.ts +1 -30
  133. package/src/blocks/stats/variants/index.ts +13 -1
  134. package/src/blocks/tabs/tab-1.schema.ts +1361 -214
  135. package/src/blocks/tabs/tab-2.schema.ts +984 -390
  136. package/src/blocks/tabs/tab-3.schema.ts +961 -95
  137. package/src/blocks/team/team-1.schema.ts +681 -564
  138. package/src/blocks/team/team-2.schema.ts +589 -935
  139. package/src/blocks/team/team-3.schema.ts +375 -284
  140. package/src/blocks/team/team-4.schema.ts +569 -355
  141. package/src/blocks/team/team-5.schema.ts +307 -111
  142. package/src/blocks/testimonial/testimonial-1.schema.ts +605 -706
  143. package/src/blocks/testimonial/testimonial-2.schema.ts +825 -1099
  144. package/src/blocks/testimonial/testimonial-3.schema.ts +580 -235
  145. package/src/blocks/testimonial/testimonial-4.schema.ts +1326 -1779
  146. package/src/blocks/testimonial/testimonial-5/variants/default.ts +179 -328
  147. package/src/blocks/testimonial/testimonial-5/variants/index.ts +9 -1
  148. package/src/blocks/testimonial/testimonial-5/variants/testimonialCentered.ts +194 -0
  149. package/src/blocks/testimonial/testimonial-5/variants/testimonialCompact.ts +188 -0
  150. package/src/blocks/testimonial/testimonial-5/variants/testimonialWithLogo.ts +275 -396
  151. package/src/blocks/testimonial/testimonial-5/variants/testimonialWithRatings.ts +254 -602
  152. package/src/blocks/testimonial/testimonial-5.schema.ts +6 -2
  153. package/src/blocks/testimonial/testimonial-6.schema.ts +962 -1234
  154. package/src/blocks/testimonial/testimonial-7.schema.ts +515 -233
  155. package/src/blocks/timeline/timeline-1.schema.ts +382 -140
  156. package/src/blocks/users/edit-profile.schema.ts +2 -8
  157. package/src/blocks/users/edit-profile.tsx +2 -3
  158. package/src/schemas/blocks.ts +2 -0
  159. package/src/blocks/features/features-1/variants/stylized-cards-v3.ts +0 -268
@@ -1,23 +1,29 @@
1
- import type { ILevoBlockBaseProps } from "@levo-so/studio";
1
+ import type { IModuleStudioConfig } from "@levo-so/core";
2
+ import type { ILevoBlockBaseProps, IStudioSource } from "@levo-so/studio";
2
3
  import {
3
4
  Avatar,
4
5
  AvatarFallbackV2,
5
6
  AvatarImageV2,
6
7
  BlogContent,
7
8
  Box,
9
+ Button,
8
10
  Container,
9
11
  Heading,
10
12
  Icon,
13
+ Image,
11
14
  Media,
12
15
  Section,
13
16
  Typography,
17
+ useBlockContext,
14
18
  usePageContext,
19
+ usePageMeta,
15
20
  } from "@levo-so/studio";
16
21
  import dayjs from "dayjs";
22
+ import { ChevronDown } from "lucide-react";
17
23
  import type React from "react";
18
24
  import { useEffect, useMemo, useState } from "react";
19
25
 
20
- import type { IBlogPost1Content } from "./blog-post-1.schema";
26
+ import { type IBlogPost1Content, MEDIA } from "./blog-post-1.schema";
21
27
  import { ClientOnly } from "./ClientOnly";
22
28
 
23
29
  interface ITocItem {
@@ -138,9 +144,77 @@ const TableOfContents: React.FC<{ headings: ITocItem[] }> = ({ headings }) => {
138
144
  );
139
145
  };
140
146
 
147
+ /** Canonical display order for social platform icons. */
148
+ const SOCIAL_ORDER = [
149
+ "instagram",
150
+ "twitter",
151
+ "linkedin",
152
+ "facebook",
153
+ "youtube",
154
+ "github",
155
+ "tiktok",
156
+ "website",
157
+ ];
158
+
159
+ const sortSocials = (entries: [string, unknown][]) =>
160
+ [...entries].sort(([a], [b]) => {
161
+ const aIdx = SOCIAL_ORDER.indexOf(a);
162
+ const bIdx = SOCIAL_ORDER.indexOf(b);
163
+ return (aIdx === -1 ? SOCIAL_ORDER.length : aIdx) - (bIdx === -1 ? SOCIAL_ORDER.length : bIdx);
164
+ });
165
+
166
+ const BIO_CHAR_LIMIT = 180;
167
+
168
+ const AuthorBioRenderer: React.FC<{
169
+ author: any;
170
+ expandedBios: Record<number, boolean>;
171
+ setExpandedBios: React.Dispatch<React.SetStateAction<Record<number, boolean>>>;
172
+ index: number;
173
+ }> = ({ author, expandedBios, setExpandedBios, index }) => {
174
+ const bio = author?.bio ?? "";
175
+ const isExpanded = expandedBios[index] ?? false;
176
+ const isTruncated = bio.length > BIO_CHAR_LIMIT;
177
+ const displayBio = !isExpanded && isTruncated ? `${bio.slice(0, BIO_CHAR_LIMIT)}…` : bio;
178
+ return (
179
+ <Box elementKey="author-bio-wrapper">
180
+ <Typography elementKey="author-bio">{displayBio}</Typography>
181
+ {isTruncated && (
182
+ <Button
183
+ elementKey="author-bio-read-more"
184
+ type="button"
185
+ onClick={() => setExpandedBios((prev) => ({ ...prev, [index]: !isExpanded }))}
186
+ >
187
+ {isExpanded ? "Read Less" : "Read More"}
188
+ <ChevronDown
189
+ style={{
190
+ float: "right",
191
+ rotate: isExpanded ? "180deg" : "0deg",
192
+ transition: "rotate 0.2s ease-in-out",
193
+ marginLeft: "4px",
194
+ marginTop: "2px",
195
+ }}
196
+ size={16}
197
+ />
198
+ </Button>
199
+ )}
200
+ </Box>
201
+ );
202
+ };
203
+
141
204
  const BlogPost1: React.FC<ILevoBlockBaseProps<IBlogPost1Content>> = () => {
205
+ const { isStudio } = useBlockContext();
142
206
  const data = usePageContext("blog");
143
- const headings = useMemo(() => extractHeadingsFromContent(data.content), [data.content]);
207
+ /**
208
+ * Tracks which author bios are expanded, keyed by author index.
209
+ * Absent keys default to collapsed.
210
+ */
211
+ const [expandedBios, setExpandedBios] = useState<Record<number, boolean>>({});
212
+ const meta = usePageMeta("blog") as {
213
+ studio_config?: (IModuleStudioConfig & {
214
+ module: IStudioSource["module"];
215
+ })[];
216
+ };
217
+ const headings = useMemo(() => extractHeadingsFromContent(data.content), [data?.content]);
144
218
 
145
219
  useEffect(() => {
146
220
  if (!data.content?.html || headings.length === 0) return;
@@ -160,6 +234,10 @@ const BlogPost1: React.FC<ILevoBlockBaseProps<IBlogPost1Content>> = () => {
160
234
  });
161
235
  }, [data.content?.html, headings]);
162
236
 
237
+ const blogAuthorStudioConfig = meta?.studio_config?.find(
238
+ (item) => !item.block_instance_id && item?.module === "blog_author",
239
+ );
240
+
163
241
  return (
164
242
  <Section elementKey="layout">
165
243
  <Container elementKey="container">
@@ -175,38 +253,57 @@ const BlogPost1: React.FC<ILevoBlockBaseProps<IBlogPost1Content>> = () => {
175
253
  <Box elementKey="title-section">
176
254
  <Heading elementKey="title">{data.title}</Heading>
177
255
  <Box elementKey="meta-info">
178
- <Box elementKey="author-container">
179
- <Avatar className="h-10 w-10">
180
- <AvatarImageV2 src={data?.authors?.[0]?.profile_picture?.location} />
181
- <AvatarFallbackV2>
182
- {[data?.authors?.[0]?.first_name, data?.authors?.[0]?.last_name]
183
- ?.map((v) => v?.[0])
256
+ {data?.authors && Array.isArray(data?.authors) && (
257
+ <Box elementKey="authors-container">
258
+ {data?.authors?.map((v: any, index: number) => {
259
+ const authorName = [v?.first_name, v?.last_name]
184
260
  .filter(Boolean)
185
- .join("")
186
- .toUpperCase()}
187
- </AvatarFallbackV2>
188
- </Avatar>
189
- <Box elementKey="author-info-wrapper">
190
- <Heading elementKey="author-name">
191
- {data?.authors?.[0]
192
- ? [data?.authors?.[0]?.first_name, data?.authors?.[0]?.last_name]
193
- .filter(Boolean)
194
- .join(" ")
195
- : "Anonymous"}
196
- </Heading>
197
- <Typography elementKey="author-description">
198
- {data?.authors?.[0]?.bio || "Author"}
199
- </Typography>
261
+ .join(" ")
262
+ .trim();
263
+ const authorAltStr = authorName
264
+ ? `Profile photo of ${authorName}`
265
+ : "Author profile photo";
266
+ return (
267
+ <Box
268
+ key={`${v?.first_name}-${v?.last_name}-${index}`}
269
+ elementKey="author-container"
270
+ >
271
+ <Avatar className="h-10 w-10">
272
+ <AvatarImageV2
273
+ src={v?.profile_picture?.location}
274
+ className="object-cover"
275
+ alt={authorAltStr}
276
+ />
277
+ <AvatarFallbackV2>
278
+ {[v?.first_name, v?.last_name]
279
+ ?.map((v) => v?.[0])
280
+ .filter(Boolean)
281
+ .join("")
282
+ .toUpperCase()}
283
+ </AvatarFallbackV2>
284
+ </Avatar>
285
+ <Box elementKey="author-info-wrapper">
286
+ <Heading elementKey="author-name">{authorName || "Anonymous"}</Heading>
287
+ <Typography elementKey="author-description">
288
+ {/* {v?.bio || "Author"} */}
289
+ {`@${v?.username}`}
290
+ </Typography>
291
+ </Box>
292
+ </Box>
293
+ );
294
+ })}
200
295
  </Box>
296
+ )}
297
+ <Box elementKey="meta-info-sub-container">
298
+ <ClientOnly>
299
+ <Typography elementKey="publish-date">
300
+ {data.published_at
301
+ ? dayjs(data.published_at ?? data.created_at).format("DD MMM, YYYY")
302
+ : ""}
303
+ </Typography>
304
+ </ClientOnly>
305
+ <Typography elementKey="reading-time">{`${data.reading_time} mins`}</Typography>
201
306
  </Box>
202
- <ClientOnly>
203
- <Typography elementKey="publish-date">
204
- {data.published_at
205
- ? dayjs(data.published_at ?? data.created_at).format("DD MMM, YYYY")
206
- : ""}
207
- </Typography>
208
- </ClientOnly>
209
- <Typography elementKey="reading-time">{`${data.reading_time} mins`}</Typography>
210
307
  </Box>
211
308
  </Box>
212
309
  </Box>
@@ -222,6 +319,133 @@ const BlogPost1: React.FC<ILevoBlockBaseProps<IBlogPost1Content>> = () => {
222
319
  <BlogContent elementKey="content" content={data.content?.html || ""} />
223
320
  </Box>
224
321
  </Box>
322
+ {data?.authors && Array.isArray(data?.authors) && (
323
+ <Box elementKey="author-cards-container">
324
+ {data?.authors?.map((author: any, index: number) => {
325
+ const authorName = [author?.first_name, author?.last_name]
326
+ .filter(Boolean)
327
+ .join(" ")
328
+ .trim();
329
+ const authorAltStr = authorName
330
+ ? `Profile photo of ${authorName}`
331
+ : "Author profile photo";
332
+
333
+ const socials = sortSocials(
334
+ Object.entries((author?.socials as Record<string, string>) ?? {}).filter(
335
+ ([_key, value]) => Boolean(value),
336
+ ),
337
+ ) as [string, string][];
338
+
339
+ return (
340
+ <Box
341
+ elementKey="author-card-section"
342
+ key={`${author?.first_name}-${author?.last_name}-${index}`}
343
+ >
344
+ <Box elementKey="author-card-inner">
345
+ {/* Left: Avatar — plain img for grayscale hover effect */}
346
+ <Image
347
+ elementKey="author-card-image"
348
+ image={author?.profile_picture ?? MEDIA}
349
+ alt={authorAltStr}
350
+ />
351
+
352
+ {/* Right: Content */}
353
+ <Box elementKey="author-card-content">
354
+ {/* Name + social row */}
355
+ <Box
356
+ elementKey="author-card-content-header"
357
+ className="mb-4 flex flex-col justify-between gap-4 sm:flex-row sm:items-center"
358
+ >
359
+ <Box elementKey="author-card-content-info">
360
+ <Heading elementKey="author-card-name">{authorName}</Heading>
361
+ <Typography elementKey="author-card-designation">
362
+ {/* {(author as { designation?: string }).designation ?? ""} */}
363
+ {`@${author?.username}`}
364
+ </Typography>
365
+ </Box>
366
+ {/* Social icons — plain HTML per exec spec */}
367
+ <Box
368
+ elementKey="author-card-content-socials"
369
+ className="flex items-center gap-3"
370
+ >
371
+ {socials?.map(([key, value]) => {
372
+ return (
373
+ <Box
374
+ config={{
375
+ action: {
376
+ type: "external_link",
377
+ value,
378
+ options: {
379
+ openInNewTab: true,
380
+ },
381
+ },
382
+ }}
383
+ elementKey={`author-card-content-socials-${key}-wrapper`}
384
+ key={key}
385
+ >
386
+ <Icon elementKey={`author-card-content-socials-${key}-icon`} />
387
+ </Box>
388
+ );
389
+ })}
390
+ </Box>
391
+ </Box>
392
+ {/* Bio with Read More / Read Less toggle */}
393
+ <AuthorBioRenderer
394
+ index={index}
395
+ author={author}
396
+ expandedBios={expandedBios}
397
+ setExpandedBios={setExpandedBios}
398
+ />
399
+ {/* Action buttons — plain HTML per exec spec */}
400
+ <Box
401
+ elementKey="author-card-ctas"
402
+ className="flex flex-wrap items-center gap-4"
403
+ >
404
+ <Button elementKey="author-card-cta-first">
405
+ Follow {author.first_name}
406
+ </Button>
407
+ <Button
408
+ elementKey="author-card-cta-second"
409
+ config={
410
+ (isStudio && blogAuthorStudioConfig?.preview_url) ||
411
+ (!isStudio && blogAuthorStudioConfig?.live_url)
412
+ ? {
413
+ action: {
414
+ type: "external_link",
415
+ value:
416
+ isStudio && blogAuthorStudioConfig.preview_url
417
+ ? blogAuthorStudioConfig.preview_url.replace(
418
+ /\{[^}]+\}/g,
419
+ author?.username,
420
+ )
421
+ : !isStudio && blogAuthorStudioConfig.live_url
422
+ ? blogAuthorStudioConfig.live_url.replace(
423
+ /\{[^}]+\}/g,
424
+ author?.username,
425
+ )
426
+ : "",
427
+ options: {},
428
+ },
429
+ }
430
+ : {}
431
+ }
432
+ >
433
+ View Profile
434
+ </Button>
435
+ </Box>
436
+ </Box>
437
+ </Box>
438
+ {/* Footer divider */}
439
+ {index === data?.authors?.length - 1 && (
440
+ <Box elementKey="author-card-section-footer">
441
+ <Typography elementKey="author-card-editorial" />
442
+ </Box>
443
+ )}
444
+ </Box>
445
+ );
446
+ })}
447
+ </Box>
448
+ )}
225
449
  </Container>
226
450
  </Section>
227
451
  );