keystone-design-bootstrap 1.0.25 → 1.0.26
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.
- package/package.json +1 -1
- package/src/design_system/components/DynamicFormFields.tsx +1 -1
- package/src/design_system/sections/social-media-grid.aman.tsx +13 -2
- package/src/design_system/sections/social-media-grid.barelux.tsx +13 -2
- package/src/design_system/sections/social-media-grid.tsx +14 -2
- package/src/tracking/MetaPixel.tsx +4 -3
- package/src/types/api/social-post.ts +16 -6
package/package.json
CHANGED
|
@@ -73,7 +73,7 @@ function renderField(
|
|
|
73
73
|
const value = phoneValues[name] ?? '';
|
|
74
74
|
const placeholder = nationalMask ? nationalMask.replace(/#/g, '0') : (field.placeholder ?? '');
|
|
75
75
|
|
|
76
|
-
const handlePhoneChange
|
|
76
|
+
const handlePhoneChange: (value: string) => void = (value) => {
|
|
77
77
|
const digits = value.replace(/\D/g, '');
|
|
78
78
|
const formatted = nationalMask ? formatDigitsToMask(digits, nationalMask) : digits;
|
|
79
79
|
setPhoneValues((prev) => ({ ...prev, [name]: formatted }));
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { PhotoWithFallback } from '../elements';
|
|
2
2
|
import type { SocialPost } from '../../types/api/social-post';
|
|
3
3
|
|
|
4
|
+
/** Get display image URLs from post: image_urls (API) or photo_attachments (ordered) */
|
|
5
|
+
function getPostImageUrls(post: SocialPost): string[] {
|
|
6
|
+
if (post.image_urls?.length) return post.image_urls;
|
|
7
|
+
const attachments = post.photo_attachments || [];
|
|
8
|
+
const sorted = [...attachments].sort((a, b) => (a.sort_order ?? 0) - (b.sort_order ?? 0));
|
|
9
|
+
return sorted
|
|
10
|
+
.map((pa) => pa.photo?.large_url || pa.photo?.original_url || pa.photo?.medium_url || pa.photo?.thumbnail_url)
|
|
11
|
+
.filter((url): url is string => Boolean(url));
|
|
12
|
+
}
|
|
13
|
+
|
|
4
14
|
interface SocialMediaGridProps {
|
|
5
15
|
socialPosts?: SocialPost[] | null;
|
|
6
16
|
title?: string;
|
|
@@ -55,7 +65,7 @@ export const SocialMediaGrid = ({
|
|
|
55
65
|
{posts.length > 0 ? (
|
|
56
66
|
<div className="grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
|
57
67
|
{posts.map((post) => {
|
|
58
|
-
const images = post
|
|
68
|
+
const images = getPostImageUrls(post);
|
|
59
69
|
const firstImage = images[0] || '';
|
|
60
70
|
const content = post.content_markdown?.replace(/[#*\[\]()]/g, '').trim() || '';
|
|
61
71
|
const platform = post.platform || 'social';
|
|
@@ -65,6 +75,7 @@ export const SocialMediaGrid = ({
|
|
|
65
75
|
<div key={post.id} className="flex flex-col bg-white overflow-hidden">
|
|
66
76
|
<div className="w-full h-64 overflow-hidden">
|
|
67
77
|
<PhotoWithFallback
|
|
78
|
+
item={post}
|
|
68
79
|
photoUrl={firstImage}
|
|
69
80
|
photoAlt={content.substring(0, 50) || 'Social post'}
|
|
70
81
|
fallbackId={post.id}
|
|
@@ -93,7 +104,7 @@ export const SocialMediaGrid = ({
|
|
|
93
104
|
|
|
94
105
|
{post.platform && (
|
|
95
106
|
<a
|
|
96
|
-
href={
|
|
107
|
+
href={post.external_post_url || `https://${platform.toLowerCase()}.com`}
|
|
97
108
|
target="_blank"
|
|
98
109
|
rel="noopener noreferrer"
|
|
99
110
|
className="font-body text-sm underline underline-offset-4 hover:no-underline mt-auto transition-colors"
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { PhotoWithFallback } from '../elements';
|
|
2
2
|
import type { SocialPost } from '../../types/api/social-post';
|
|
3
3
|
|
|
4
|
+
/** Get display image URLs from post: image_urls (API) or photo_attachments (ordered) */
|
|
5
|
+
function getPostImageUrls(post: SocialPost): string[] {
|
|
6
|
+
if (post.image_urls?.length) return post.image_urls;
|
|
7
|
+
const attachments = post.photo_attachments || [];
|
|
8
|
+
const sorted = [...attachments].sort((a, b) => (a.sort_order ?? 0) - (b.sort_order ?? 0));
|
|
9
|
+
return sorted
|
|
10
|
+
.map((pa) => pa.photo?.large_url || pa.photo?.original_url || pa.photo?.medium_url || pa.photo?.thumbnail_url)
|
|
11
|
+
.filter((url): url is string => Boolean(url));
|
|
12
|
+
}
|
|
13
|
+
|
|
4
14
|
interface SocialMediaGridProps {
|
|
5
15
|
socialPosts?: SocialPost[] | null;
|
|
6
16
|
title?: string;
|
|
@@ -63,7 +73,7 @@ export const SocialMediaGrid = ({
|
|
|
63
73
|
{posts.length > 0 ? (
|
|
64
74
|
<div className="grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
|
65
75
|
{posts.map((post) => {
|
|
66
|
-
const images = post
|
|
76
|
+
const images = getPostImageUrls(post);
|
|
67
77
|
const firstImage = images[0] || '';
|
|
68
78
|
const content = post.content_markdown?.replace(/[#*\[\]()]/g, '').trim() || '';
|
|
69
79
|
const platform = post.platform || 'social';
|
|
@@ -73,6 +83,7 @@ export const SocialMediaGrid = ({
|
|
|
73
83
|
<div key={post.id} className="flex flex-col bg-white rounded-2xl overflow-hidden shadow-sm hover:shadow-md transition-shadow duration-300">
|
|
74
84
|
<div className="w-full h-64 overflow-hidden">
|
|
75
85
|
<PhotoWithFallback
|
|
86
|
+
item={post}
|
|
76
87
|
photoUrl={firstImage}
|
|
77
88
|
photoAlt={content.substring(0, 50) || ''}
|
|
78
89
|
fallbackId={post.id}
|
|
@@ -101,7 +112,7 @@ export const SocialMediaGrid = ({
|
|
|
101
112
|
|
|
102
113
|
{post.platform && (
|
|
103
114
|
<a
|
|
104
|
-
href={
|
|
115
|
+
href={post.external_post_url || `https://${platform.toLowerCase()}.com`}
|
|
105
116
|
target="_blank"
|
|
106
117
|
rel="noopener noreferrer"
|
|
107
118
|
className="font-body text-sm underline underline-offset-4 hover:no-underline mt-auto transition-colors"
|
|
@@ -8,6 +8,16 @@ import { Button, PaginationPageMinimalCenter, PhotoWithFallback, RoundButton } f
|
|
|
8
8
|
import { cx } from '../../utils/cx';
|
|
9
9
|
import type { SocialPost } from '../../types/api/social-post';
|
|
10
10
|
|
|
11
|
+
/** Get display image URLs from post: image_urls (API) or photo_attachments (ordered) */
|
|
12
|
+
function getPostImageUrls(post: SocialPost): string[] {
|
|
13
|
+
if (post.image_urls?.length) return post.image_urls;
|
|
14
|
+
const attachments = post.photo_attachments || [];
|
|
15
|
+
const sorted = [...attachments].sort((a, b) => (a.sort_order ?? 0) - (b.sort_order ?? 0));
|
|
16
|
+
return sorted
|
|
17
|
+
.map((pa) => pa.photo?.large_url || pa.photo?.original_url || pa.photo?.medium_url || pa.photo?.thumbnail_url)
|
|
18
|
+
.filter((url): url is string => Boolean(url));
|
|
19
|
+
}
|
|
20
|
+
|
|
11
21
|
interface SocialMediaGridProps {
|
|
12
22
|
socialPosts?: SocialPost[] | null;
|
|
13
23
|
title?: string;
|
|
@@ -79,7 +89,7 @@ export const SocialMediaGrid = ({
|
|
|
79
89
|
<>
|
|
80
90
|
<ul className="grid grid-cols-1 gap-x-8 gap-y-10 sm:grid-cols-2 md:gap-y-12 lg:grid-cols-3 xl:grid-cols-4">
|
|
81
91
|
{paginatedPosts.map((post, index) => {
|
|
82
|
-
const images = post
|
|
92
|
+
const images = getPostImageUrls(post);
|
|
83
93
|
const hasMultipleImages = images.length > 1;
|
|
84
94
|
const firstImage = images[0];
|
|
85
95
|
const content = post.content_markdown?.replace(/[#*\[\]()]/g, '').trim() || '';
|
|
@@ -117,6 +127,7 @@ export const SocialMediaGrid = ({
|
|
|
117
127
|
</Carousel.Root>
|
|
118
128
|
) : (
|
|
119
129
|
<PhotoWithFallback
|
|
130
|
+
item={post}
|
|
120
131
|
photoUrl={firstImage}
|
|
121
132
|
photoAlt={`${post.platform} post`}
|
|
122
133
|
fallbackId={`social-post-${post.id || index}`}
|
|
@@ -125,6 +136,7 @@ export const SocialMediaGrid = ({
|
|
|
125
136
|
)
|
|
126
137
|
) : (
|
|
127
138
|
<PhotoWithFallback
|
|
139
|
+
item={post}
|
|
128
140
|
photoUrl={undefined}
|
|
129
141
|
photoAlt={`${post.platform} post`}
|
|
130
142
|
fallbackId={`social-post-${post.id || index}`}
|
|
@@ -154,7 +166,7 @@ export const SocialMediaGrid = ({
|
|
|
154
166
|
)}
|
|
155
167
|
|
|
156
168
|
<Button
|
|
157
|
-
href={`https://${post.platform.toLowerCase()}.com`}
|
|
169
|
+
href={post.external_post_url || `https://${post.platform.toLowerCase()}.com`}
|
|
158
170
|
target="_blank"
|
|
159
171
|
rel="noopener noreferrer"
|
|
160
172
|
color="link-color"
|
|
@@ -27,12 +27,12 @@ export type MetaPixelProps = {
|
|
|
27
27
|
* provides a meta_pixel_id.
|
|
28
28
|
*/
|
|
29
29
|
export function MetaPixel({ pixelId }: MetaPixelProps) {
|
|
30
|
-
|
|
30
|
+
const raw = typeof pixelId === 'string' ? pixelId.trim() : '';
|
|
31
|
+
const id = raw && raw !== 'null' && /^\d+$/.test(raw) ? raw : '';
|
|
32
|
+
if (!id) {
|
|
31
33
|
return null;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
const id = pixelId.trim();
|
|
35
|
-
|
|
36
36
|
return (
|
|
37
37
|
<>
|
|
38
38
|
<Script
|
|
@@ -41,6 +41,7 @@ export function MetaPixel({ pixelId }: MetaPixelProps) {
|
|
|
41
41
|
dangerouslySetInnerHTML={{ __html: PIXEL_SCRIPT(id) }}
|
|
42
42
|
/>
|
|
43
43
|
<noscript>
|
|
44
|
+
{/* eslint-disable-next-line @next/next/no-img-element -- 1x1 tracking pixel for no-JS fallback; next/image not applicable in noscript */}
|
|
44
45
|
<img
|
|
45
46
|
height={1}
|
|
46
47
|
width={1}
|
|
@@ -1,21 +1,31 @@
|
|
|
1
|
-
|
|
1
|
+
import { PhotoAttachment } from './photos';
|
|
2
|
+
|
|
3
|
+
// Social post type definitions (aligned with Rails SocialPostSerializer)
|
|
2
4
|
export interface SocialPost {
|
|
3
5
|
id: number;
|
|
4
6
|
platform: string;
|
|
5
7
|
content_markdown: string;
|
|
6
|
-
|
|
8
|
+
posted_at: string;
|
|
9
|
+
status?: string;
|
|
10
|
+
created_at: string;
|
|
11
|
+
updated_at: string;
|
|
12
|
+
/** Image URLs from photo_attachments (preferred for display) */
|
|
13
|
+
image_urls?: string[];
|
|
14
|
+
/** Photo attachments (same pattern as blog post, team member, etc.) */
|
|
15
|
+
photo_attachments?: PhotoAttachment[];
|
|
16
|
+
/** Legacy; prefer image_urls / photo_attachments */
|
|
17
|
+
media_urls?: {
|
|
7
18
|
images?: string[];
|
|
8
19
|
videos?: string[];
|
|
9
20
|
};
|
|
10
|
-
engagement_metrics
|
|
21
|
+
engagement_metrics?: {
|
|
11
22
|
likes?: number;
|
|
12
23
|
comments?: number;
|
|
13
24
|
shares?: number;
|
|
14
25
|
views?: number;
|
|
15
26
|
};
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
updated_at: string;
|
|
27
|
+
/** Permalink to the post on the platform when available; only link when present to avoid broken links */
|
|
28
|
+
external_post_url?: string | null;
|
|
19
29
|
}
|
|
20
30
|
|
|
21
31
|
export interface SocialPostParams {
|