@flamingo-stack/openframe-frontend-core 0.0.310 → 0.0.311
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/dist/{chunk-PLFQJ5E7.cjs → chunk-2EILUSCY.cjs} +37 -37
- package/dist/{chunk-PLFQJ5E7.cjs.map → chunk-2EILUSCY.cjs.map} +1 -1
- package/dist/{chunk-E7FIV5LH.js → chunk-3JT6LRGB.js} +2 -2
- package/dist/{chunk-6RMANFX7.cjs → chunk-BZA3APSK.cjs} +30 -30
- package/dist/{chunk-6RMANFX7.cjs.map → chunk-BZA3APSK.cjs.map} +1 -1
- package/dist/{chunk-AIHM4TT7.cjs → chunk-CKNJYFLB.cjs} +5 -5
- package/dist/{chunk-AIHM4TT7.cjs.map → chunk-CKNJYFLB.cjs.map} +1 -1
- package/dist/{chunk-46KRPHHL.js → chunk-DH2R5ZMA.js} +2 -2
- package/dist/{chunk-QPTJOLAP.js → chunk-DXOGAFXQ.js} +2 -2
- package/dist/{chunk-4RHOLPFU.cjs → chunk-GAKX3IB4.cjs} +14 -14
- package/dist/{chunk-4RHOLPFU.cjs.map → chunk-GAKX3IB4.cjs.map} +1 -1
- package/dist/{chunk-5OFBD6EQ.js → chunk-HLQQN2H2.js} +2 -2
- package/dist/{chunk-6TRTIHGW.cjs → chunk-KNUY2CAL.cjs} +104 -87
- package/dist/chunk-KNUY2CAL.cjs.map +1 -0
- package/dist/{chunk-WSEK6W4B.js → chunk-KYQGJD2Q.js} +2 -2
- package/dist/{chunk-2YYAKVL7.cjs → chunk-L4ND4ZAB.cjs} +12 -12
- package/dist/{chunk-2YYAKVL7.cjs.map → chunk-L4ND4ZAB.cjs.map} +1 -1
- package/dist/{chunk-ER4CMF47.js → chunk-NS2WLRTI.js} +50 -33
- package/dist/chunk-NS2WLRTI.js.map +1 -0
- package/dist/{chunk-CKFHYXSJ.cjs → chunk-P5QS2625.cjs} +7 -7
- package/dist/{chunk-CKFHYXSJ.cjs.map → chunk-P5QS2625.cjs.map} +1 -1
- package/dist/{chunk-6SBJVDH3.js → chunk-PYKMJPWX.js} +4 -4
- package/dist/{chunk-N3YPIZBH.js → chunk-RUJCGYDL.js} +2 -2
- package/dist/{chunk-4RI7S6ZD.cjs → chunk-WLW35D5O.cjs} +9 -9
- package/dist/{chunk-4RI7S6ZD.cjs.map → chunk-WLW35D5O.cjs.map} +1 -1
- package/dist/{chunk-J3YKVLQ5.cjs → chunk-YVTTTQKF.cjs} +26 -26
- package/dist/{chunk-J3YKVLQ5.cjs.map → chunk-YVTTTQKF.cjs.map} +1 -1
- package/dist/{chunk-QWMYOUGP.js → chunk-ZRUQRXCP.js} +2 -2
- package/dist/components/case-studies/index.cjs +8 -8
- package/dist/components/case-studies/index.js +2 -2
- package/dist/components/chat/entity-cards/program-card.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +2 -2
- package/dist/components/chat/index.js +1 -1
- package/dist/components/chat/types/entities/investor-update.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/docs/index.cjs +5 -5
- package/dist/components/docs/index.js +4 -4
- package/dist/components/embeds/index.cjs +3 -3
- package/dist/components/embeds/index.js +2 -2
- package/dist/components/faq/index.cjs +3 -3
- package/dist/components/faq/index.js +2 -2
- package/dist/components/features/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/index.cjs +172 -172
- package/dist/components/index.js +8 -8
- package/dist/components/logs-list.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/onboarding-guides/index.cjs +23 -23
- package/dist/components/onboarding-guides/index.js +3 -3
- package/dist/components/related-content/index.cjs +3 -3
- package/dist/components/related-content/index.js +2 -2
- package/dist/components/tickets/index.cjs +60 -60
- package/dist/components/tickets/index.js +3 -3
- package/dist/components/ui/device-card.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +2 -2
- package/dist/components/ui/index.js +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.js +1 -1
- package/dist/utils/date-utils.d.ts.map +1 -1
- package/dist/utils/format.d.ts +12 -3
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/index.cjs +26 -14
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js +26 -14
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/chat/entity-cards/blog-card.tsx +2 -2
- package/src/components/chat/entity-cards/dispatch.tsx +1 -1
- package/src/components/chat/entity-cards/program-card.tsx +17 -4
- package/src/components/chat/types/entities/investor-update.ts +2 -0
- package/src/components/logs-list.tsx +8 -6
- package/src/components/ui/device-card.tsx +8 -6
- package/src/utils/date-utils.ts +27 -14
- package/src/utils/format.ts +27 -8
- package/dist/chunk-6TRTIHGW.cjs.map +0 -1
- package/dist/chunk-ER4CMF47.js.map +0 -1
- /package/dist/{chunk-E7FIV5LH.js.map → chunk-3JT6LRGB.js.map} +0 -0
- /package/dist/{chunk-46KRPHHL.js.map → chunk-DH2R5ZMA.js.map} +0 -0
- /package/dist/{chunk-QPTJOLAP.js.map → chunk-DXOGAFXQ.js.map} +0 -0
- /package/dist/{chunk-5OFBD6EQ.js.map → chunk-HLQQN2H2.js.map} +0 -0
- /package/dist/{chunk-WSEK6W4B.js.map → chunk-KYQGJD2Q.js.map} +0 -0
- /package/dist/{chunk-6SBJVDH3.js.map → chunk-PYKMJPWX.js.map} +0 -0
- /package/dist/{chunk-N3YPIZBH.js.map → chunk-RUJCGYDL.js.map} +0 -0
- /package/dist/{chunk-QWMYOUGP.js.map → chunk-ZRUQRXCP.js.map} +0 -0
package/package.json
CHANGED
|
@@ -129,7 +129,7 @@ export function BlogCard({
|
|
|
129
129
|
|
|
130
130
|
if (size === 'sm') {
|
|
131
131
|
const dateStr = post.published_at
|
|
132
|
-
? new Date(post.published_at).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })
|
|
132
|
+
? new Date(post.published_at).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', timeZone: 'UTC' })
|
|
133
133
|
: ''
|
|
134
134
|
const firstCategory = post.categories?.find((c) => c && c.name)?.name
|
|
135
135
|
return (
|
|
@@ -179,7 +179,7 @@ export function BlogCard({
|
|
|
179
179
|
|
|
180
180
|
// Default: full vertical card.
|
|
181
181
|
const dateStr = post.published_at
|
|
182
|
-
? new Date(post.published_at).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })
|
|
182
|
+
? new Date(post.published_at).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', timeZone: 'UTC' })
|
|
183
183
|
: ''
|
|
184
184
|
return (
|
|
185
185
|
<article
|
|
@@ -893,7 +893,7 @@ function ProgramChatCard({
|
|
|
893
893
|
) {
|
|
894
894
|
typeMeta = item.location_name
|
|
895
895
|
} else if (configKey === 'webinar' && item?.start_at) {
|
|
896
|
-
const time = formatTimeWithTimezone(item.start_at, null)
|
|
896
|
+
const time = formatTimeWithTimezone(item.start_at, item.timezone ?? null)
|
|
897
897
|
const dur = formatDurationFromRange(item.start_at, item.end_at)
|
|
898
898
|
typeMeta = dur ? `${time} · ${dur}` : time
|
|
899
899
|
}
|
|
@@ -45,6 +45,19 @@ import { useEntityCardPlaceholder } from './use-entity-card-placeholder'
|
|
|
45
45
|
|
|
46
46
|
type CardSize = 'default' | 'sm'
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Format a Date with date-fns pinned to UTC. `date-fns` `format()` reads the
|
|
50
|
+
* runtime's LOCAL wall-clock, so the same instant renders differently on the
|
|
51
|
+
* server (Vercel = UTC) and the client (visitor tz) → React #418 hydration
|
|
52
|
+
* mismatch. Shifting by the local offset before formatting emits the UTC
|
|
53
|
+
* wall-clock on every machine, so server and client agree. Mirrors the helper
|
|
54
|
+
* in the hub's `program-header.tsx` (kept local — the lib has no date-fns-tz
|
|
55
|
+
* dep) and the repo-wide "pin program dates to UTC" convention.
|
|
56
|
+
*/
|
|
57
|
+
function formatUtc(date: Date, fmt: string): string {
|
|
58
|
+
return format(new Date(date.getTime() + date.getTimezoneOffset() * 60_000), fmt)
|
|
59
|
+
}
|
|
60
|
+
|
|
48
61
|
export function ProgramCardSkeleton({ size = 'default' }: { size?: CardSize }) {
|
|
49
62
|
if (size === 'sm') {
|
|
50
63
|
return (
|
|
@@ -226,7 +239,7 @@ export function ProgramCard<T extends BaseProgramItem>({
|
|
|
226
239
|
|
|
227
240
|
if (size === 'sm') {
|
|
228
241
|
const itemDate = (() => {
|
|
229
|
-
try { return
|
|
242
|
+
try { return formatUtc(new Date(item.date), 'MMM d, yyyy') } catch { return '' }
|
|
230
243
|
})()
|
|
231
244
|
const compactCover = coverImage || placeholderUrl || null
|
|
232
245
|
let typeMeta: string | null = null
|
|
@@ -238,7 +251,7 @@ export function ProgramCard<T extends BaseProgramItem>({
|
|
|
238
251
|
if (typeof loc === 'string' && loc.trim().length > 0) typeMeta = loc
|
|
239
252
|
} else if (config.type === 'webinar' && 'start_at' in item) {
|
|
240
253
|
const w = item as any
|
|
241
|
-
const time = formatTimeWithTimezone(w.start_at, null)
|
|
254
|
+
const time = formatTimeWithTimezone(w.start_at, w.timezone ?? null)
|
|
242
255
|
const dur = formatDurationFromRange(w.start_at, w.end_at)
|
|
243
256
|
typeMeta = dur ? `${time} · ${dur}` : time
|
|
244
257
|
}
|
|
@@ -293,7 +306,7 @@ export function ProgramCard<T extends BaseProgramItem>({
|
|
|
293
306
|
}
|
|
294
307
|
|
|
295
308
|
const itemDate = new Date(item.date)
|
|
296
|
-
const dateFormat =
|
|
309
|
+
const dateFormat = formatUtc(itemDate, 'EEEE d MMMM')
|
|
297
310
|
|
|
298
311
|
const defaultRenderMeta = () => {
|
|
299
312
|
if (config.type === 'podcast' && 'duration_seconds' in item && !isScheduled) {
|
|
@@ -320,7 +333,7 @@ export function ProgramCard<T extends BaseProgramItem>({
|
|
|
320
333
|
<>
|
|
321
334
|
<Video className="w-4 h-4 text-ods-text-secondary" />
|
|
322
335
|
<span className="font-['DM_Sans'] text-ods-text-secondary">
|
|
323
|
-
{formatTimeWithTimezone(webinarItem.start_at, null)}
|
|
336
|
+
{formatTimeWithTimezone(webinarItem.start_at, webinarItem.timezone ?? null)}
|
|
324
337
|
{duration && ` · ${duration}`}
|
|
325
338
|
</span>
|
|
326
339
|
{webinarItem.timezone && (
|
|
@@ -93,6 +93,8 @@ export function formatInvestorUpdatePeriod(
|
|
|
93
93
|
new Date(d).toLocaleDateString('en-US', {
|
|
94
94
|
month: options?.monthFormat || 'short',
|
|
95
95
|
year: 'numeric',
|
|
96
|
+
// Pin to UTC so SSR (Vercel = UTC) and the client agree (React #418).
|
|
97
|
+
timeZone: 'UTC',
|
|
96
98
|
});
|
|
97
99
|
const s = start ? fmt(start) : '?';
|
|
98
100
|
const e = end ? fmt(end) : '?';
|
|
@@ -7,12 +7,14 @@ import { ToolIcon } from './tool-icon'
|
|
|
7
7
|
const formatTimestamp = (timestamp: string | Date): string => {
|
|
8
8
|
const date = timestamp instanceof Date ? timestamp : new Date(timestamp)
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
10
|
+
// UTC getters so the timestamp is identical on server (UTC) and client
|
|
11
|
+
// (local) — otherwise React #418 hydration mismatch.
|
|
12
|
+
const year = date.getUTCFullYear()
|
|
13
|
+
const month = String(date.getUTCMonth() + 1).padStart(2, '0')
|
|
14
|
+
const day = String(date.getUTCDate()).padStart(2, '0')
|
|
15
|
+
const hours = String(date.getUTCHours()).padStart(2, '0')
|
|
16
|
+
const minutes = String(date.getUTCMinutes()).padStart(2, '0')
|
|
17
|
+
|
|
16
18
|
return `${year}/${month}/${day},${hours}:${minutes}`
|
|
17
19
|
}
|
|
18
20
|
|
|
@@ -66,12 +66,14 @@ export function DeviceCard({
|
|
|
66
66
|
if (!lastSeen) return null
|
|
67
67
|
|
|
68
68
|
const date = typeof lastSeen === 'string' ? new Date(lastSeen) : lastSeen
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
|
|
69
|
+
// UTC getters so "last seen" is identical on server (UTC) and client
|
|
70
|
+
// (local) — otherwise React #418 hydration mismatch.
|
|
71
|
+
const year = date.getUTCFullYear()
|
|
72
|
+
const month = String(date.getUTCMonth() + 1).padStart(2, '0')
|
|
73
|
+
const day = String(date.getUTCDate()).padStart(2, '0')
|
|
74
|
+
const hours = String(date.getUTCHours()).padStart(2, '0')
|
|
75
|
+
const minutes = String(date.getUTCMinutes()).padStart(2, '0')
|
|
76
|
+
|
|
75
77
|
return `${year}/${month}/${day}, ${hours}:${minutes}`
|
|
76
78
|
}
|
|
77
79
|
|
package/src/utils/date-utils.ts
CHANGED
|
@@ -17,9 +17,11 @@ export function formatTicketRelativeTime(iso: string): string {
|
|
|
17
17
|
if (diffMin < 60) return `${diffMin} min ago`
|
|
18
18
|
const diffHours = Math.floor(diffMin / 60)
|
|
19
19
|
if (diffHours < 24) return diffHours === 1 ? '1 hour ago' : `${diffHours} hours ago`
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
20
|
+
// UTC getters so the MM/DD/YYYY tail is identical on server (UTC) and client
|
|
21
|
+
// (local) — otherwise React #418 hydration mismatch.
|
|
22
|
+
const mm = String(date.getUTCMonth() + 1).padStart(2, '0')
|
|
23
|
+
const dd = String(date.getUTCDate()).padStart(2, '0')
|
|
24
|
+
const yyyy = date.getUTCFullYear()
|
|
23
25
|
return `${mm}/${dd}/${yyyy}`
|
|
24
26
|
}
|
|
25
27
|
|
|
@@ -29,11 +31,13 @@ export function formatTicketRelativeTime(iso: string): string {
|
|
|
29
31
|
export function formatTicketFullTimestamp(iso: string): string {
|
|
30
32
|
const date = new Date(iso)
|
|
31
33
|
if (Number.isNaN(date.getTime())) return ''
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
const
|
|
34
|
+
// UTC getters so the tooltip timestamp is identical on server (UTC) and
|
|
35
|
+
// client (local) — otherwise React #418 hydration mismatch.
|
|
36
|
+
const mm = String(date.getUTCMonth() + 1).padStart(2, '0')
|
|
37
|
+
const dd = String(date.getUTCDate()).padStart(2, '0')
|
|
38
|
+
const yyyy = date.getUTCFullYear()
|
|
39
|
+
let hours = date.getUTCHours()
|
|
40
|
+
const minutes = String(date.getUTCMinutes()).padStart(2, '0')
|
|
37
41
|
const ampm = hours >= 12 ? 'PM' : 'AM'
|
|
38
42
|
hours = hours % 12 || 12
|
|
39
43
|
return `${mm}/${dd}/${yyyy}, ${hours}:${minutes} ${ampm}`
|
|
@@ -88,11 +92,14 @@ export function formatRelativeTime(timestamp: string | Date): string {
|
|
|
88
92
|
return `${weeks}w ago`;
|
|
89
93
|
}
|
|
90
94
|
|
|
91
|
-
// Older than 30 days - show formatted date
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
// Older than 30 days - show formatted date, pinned to UTC so SSR (UTC) and
|
|
96
|
+
// the client agree (React #418). Year comparison also uses UTC for the same
|
|
97
|
+
// reason (avoids a server/client split right at a year boundary).
|
|
98
|
+
return targetTime.toLocaleDateString('en-US', {
|
|
99
|
+
month: 'short',
|
|
94
100
|
day: 'numeric',
|
|
95
|
-
year: targetTime.
|
|
101
|
+
year: targetTime.getUTCFullYear() !== now.getUTCFullYear() ? 'numeric' : undefined,
|
|
102
|
+
timeZone: 'UTC',
|
|
96
103
|
});
|
|
97
104
|
}
|
|
98
105
|
|
|
@@ -118,9 +125,12 @@ export function formatAbsoluteDate(
|
|
|
118
125
|
year: 'numeric',
|
|
119
126
|
month: 'short',
|
|
120
127
|
day: 'numeric',
|
|
128
|
+
// Pin to UTC so SSR (Vercel = UTC) and the client agree (React #418).
|
|
129
|
+
// Caller can override via `options`.
|
|
130
|
+
timeZone: 'UTC',
|
|
121
131
|
...options
|
|
122
132
|
};
|
|
123
|
-
|
|
133
|
+
|
|
124
134
|
return targetTime.toLocaleDateString('en-US', defaultOptions);
|
|
125
135
|
}
|
|
126
136
|
|
|
@@ -148,9 +158,12 @@ export function formatDateTime(
|
|
|
148
158
|
day: 'numeric',
|
|
149
159
|
hour: 'numeric',
|
|
150
160
|
minute: '2-digit',
|
|
161
|
+
// Pin to UTC so SSR (Vercel = UTC) and the client agree (React #418).
|
|
162
|
+
// Caller can override via `options`.
|
|
163
|
+
timeZone: 'UTC',
|
|
151
164
|
...options
|
|
152
165
|
};
|
|
153
|
-
|
|
166
|
+
|
|
154
167
|
return targetTime.toLocaleDateString('en-US', defaultOptions);
|
|
155
168
|
}
|
|
156
169
|
|
package/src/utils/format.ts
CHANGED
|
@@ -24,7 +24,10 @@ export function formatDate(
|
|
|
24
24
|
return "Invalid Date"
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
// Pin to UTC by default so SSR (Vercel = UTC) and the client agree (React
|
|
28
|
+
// #418 hydration mismatch). A caller can still override by passing its own
|
|
29
|
+
// `timeZone` in `options`.
|
|
30
|
+
return dateObj.toLocaleDateString("en-US", { timeZone: "UTC", ...options })
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
/**
|
|
@@ -223,9 +226,18 @@ export function formatDurationCompact(seconds: number | null | undefined): strin
|
|
|
223
226
|
}
|
|
224
227
|
|
|
225
228
|
/**
|
|
226
|
-
* Format time
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
+
* Format a webinar/event start time as the wall-clock in its OWN timezone.
|
|
230
|
+
*
|
|
231
|
+
* `date` is an instant (UTC timestamp / ISO string). `timezone` is the event's
|
|
232
|
+
* IANA zone (e.g. `'America/New_York'`). Rendering in that explicit zone makes
|
|
233
|
+
* the server (Vercel = UTC) and the visitor's browser emit the SAME text —
|
|
234
|
+
* fixing the React #418 hydration mismatch — AND shows the true event time
|
|
235
|
+
* (4:00 PM EDT, not the 8:00 PM UTC a plain UTC pin would show, nor the
|
|
236
|
+
* viewer-local time the old unpinned call produced). Falls back to UTC when no
|
|
237
|
+
* zone is given, and tolerates a non-IANA label (legacy data) without throwing.
|
|
238
|
+
* The zone LABEL is rendered separately by callers, so it is never appended here.
|
|
239
|
+
*
|
|
240
|
+
* Returns: "4:00 PM"
|
|
229
241
|
*/
|
|
230
242
|
export function formatTimeWithTimezone(
|
|
231
243
|
date: Date | string | null | undefined,
|
|
@@ -234,13 +246,20 @@ export function formatTimeWithTimezone(
|
|
|
234
246
|
if (!date) return '';
|
|
235
247
|
|
|
236
248
|
const dateObj = typeof date === 'string' ? new Date(date) : date;
|
|
237
|
-
const
|
|
249
|
+
const opts: Intl.DateTimeFormatOptions = {
|
|
238
250
|
hour: 'numeric',
|
|
239
251
|
minute: '2-digit',
|
|
240
252
|
hour12: true,
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
253
|
+
timeZone: timezone || 'UTC',
|
|
254
|
+
};
|
|
255
|
+
try {
|
|
256
|
+
return dateObj.toLocaleTimeString('en-US', opts);
|
|
257
|
+
} catch {
|
|
258
|
+
// Non-IANA `timezone` (e.g. a bare "EST" label) makes Intl throw a
|
|
259
|
+
// RangeError. Fall back to a UTC-pinned render so we stay deterministic
|
|
260
|
+
// (still no #418) instead of crashing.
|
|
261
|
+
return dateObj.toLocaleTimeString('en-US', { ...opts, timeZone: 'UTC' });
|
|
262
|
+
}
|
|
244
263
|
}
|
|
245
264
|
|
|
246
265
|
/**
|