@fluid-app/portal-sdk 0.1.239 → 0.1.241
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/{CalendarWidget-BXxNxHDV.mjs → CalendarWidget-C03VcDLQ.mjs} +2 -2
- package/dist/{CalendarWidget-BXxNxHDV.mjs.map → CalendarWidget-C03VcDLQ.mjs.map} +1 -1
- package/dist/{CalendarWidget-VjQXZW8B.cjs → CalendarWidget-C4Dv_TUk.cjs} +2 -2
- package/dist/{CalendarWidget-VjQXZW8B.cjs.map → CalendarWidget-C4Dv_TUk.cjs.map} +1 -1
- package/dist/{CardWidget-BNkMlxQ3.mjs → CardWidget-2wcjCf2M.mjs} +2 -2
- package/dist/{CardWidget-BNkMlxQ3.mjs.map → CardWidget-2wcjCf2M.mjs.map} +1 -1
- package/dist/{CardWidget-CraWMttj.cjs → CardWidget-BQl-8Xrt.cjs} +3 -3
- package/dist/{CardWidget-C3I9OwgS.cjs → CardWidget-D-KEikO_.cjs} +2 -2
- package/dist/{CardWidget-C3I9OwgS.cjs.map → CardWidget-D-KEikO_.cjs.map} +1 -1
- package/dist/{CarouselWidget-p8Z6L-G5.mjs → CarouselWidget-Cn5P4FVN.mjs} +92 -83
- package/dist/CarouselWidget-Cn5P4FVN.mjs.map +1 -0
- package/dist/{CarouselWidget-CiKlooUF.cjs → CarouselWidget-Cu1b3ERq.cjs} +92 -83
- package/dist/CarouselWidget-Cu1b3ERq.cjs.map +1 -0
- package/dist/{CatchUpWidget-B9DQlzd_.mjs → CatchUpWidget-B9CI7lq0.mjs} +2 -2
- package/dist/{CatchUpWidget-B9DQlzd_.mjs.map → CatchUpWidget-B9CI7lq0.mjs.map} +1 -1
- package/dist/{CatchUpWidget-SY7hOHk6.cjs → CatchUpWidget-BzgybvuC.cjs} +2 -2
- package/dist/{CatchUpWidget-SY7hOHk6.cjs.map → CatchUpWidget-BzgybvuC.cjs.map} +1 -1
- package/dist/ContainerWidget-CDbB8c0E.cjs +8 -0
- package/dist/{ContainerWidget-CpwvcxFm.mjs → ContainerWidget-DNenbORS.mjs} +2 -2
- package/dist/{ContainerWidget-CpwvcxFm.mjs.map → ContainerWidget-DNenbORS.mjs.map} +1 -1
- package/dist/{ContainerWidget-BXpdj06u.cjs → ContainerWidget-DbAhQKbY.cjs} +2 -2
- package/dist/{ContainerWidget-BXpdj06u.cjs.map → ContainerWidget-DbAhQKbY.cjs.map} +1 -1
- package/dist/{FluidProvider-DL5rChtj.mjs → FluidProvider-BwMwMCxW.mjs} +23 -23
- package/dist/{FluidProvider-DL5rChtj.mjs.map → FluidProvider-BwMwMCxW.mjs.map} +1 -1
- package/dist/{FluidProvider-D_PHVlUU.cjs → FluidProvider-eT4gA6Io.cjs} +23 -23
- package/dist/{FluidProvider-D_PHVlUU.cjs.map → FluidProvider-eT4gA6Io.cjs.map} +1 -1
- package/dist/{LayoutWidget-BIfNHlVE.cjs → LayoutWidget-Bt2I2XMy.cjs} +2 -2
- package/dist/{LayoutWidget-BIfNHlVE.cjs.map → LayoutWidget-Bt2I2XMy.cjs.map} +1 -1
- package/dist/{LayoutWidget-LdF_cKrB.cjs → LayoutWidget-DKqZgCMu.cjs} +3 -3
- package/dist/{LayoutWidget-CC3oK78H.mjs → LayoutWidget-UI5fbsx4.mjs} +2 -2
- package/dist/{LayoutWidget-CC3oK78H.mjs.map → LayoutWidget-UI5fbsx4.mjs.map} +1 -1
- package/dist/{MessagingScreen-Dxy2VQbO.cjs → MessagingScreen-BHYX1Kc6.cjs} +14 -14
- package/dist/{MessagingScreen-mt1u3Bs1.mjs → MessagingScreen-CZddjqma.mjs} +2 -2
- package/dist/{MessagingScreen-mt1u3Bs1.mjs.map → MessagingScreen-CZddjqma.mjs.map} +1 -1
- package/dist/{MessagingScreen-Dkh3Dsp7.cjs → MessagingScreen-D3S230Ba.cjs} +2 -2
- package/dist/{MessagingScreen-Dkh3Dsp7.cjs.map → MessagingScreen-D3S230Ba.cjs.map} +1 -1
- package/dist/{MySiteWidget-BnjPrQxE.mjs → MySiteWidget-CQNASVaF.mjs} +2 -2
- package/dist/{MySiteWidget-BnjPrQxE.mjs.map → MySiteWidget-CQNASVaF.mjs.map} +1 -1
- package/dist/{MySiteWidget-C8eFWHOT.cjs → MySiteWidget-CYiH2lmX.cjs} +2 -2
- package/dist/{MySiteWidget-C8eFWHOT.cjs.map → MySiteWidget-CYiH2lmX.cjs.map} +1 -1
- package/dist/{PointsWidget-C2KB4k48.mjs → PointsWidget-BwA6aGVZ.mjs} +5 -8
- package/dist/PointsWidget-BwA6aGVZ.mjs.map +1 -0
- package/dist/{PointsWidget-DLp-PYus.cjs → PointsWidget-D1FV8l1h.cjs} +5 -8
- package/dist/PointsWidget-D1FV8l1h.cjs.map +1 -0
- package/dist/{ProfileScreen-ChCZZ91o.cjs → ProfileScreen-BYJj4D1W.cjs} +14 -14
- package/dist/{ProfileScreen-BMe-dQi7.cjs → ProfileScreen-DAujb81k.cjs} +2 -2
- package/dist/{ProfileScreen-BMe-dQi7.cjs.map → ProfileScreen-DAujb81k.cjs.map} +1 -1
- package/dist/{ProfileScreen-_1GlBr7z.mjs → ProfileScreen-DlMEqXXg.mjs} +2 -2
- package/dist/{ProfileScreen-_1GlBr7z.mjs.map → ProfileScreen-DlMEqXXg.mjs.map} +1 -1
- package/dist/{RecentActivityWidget-DelPdiwR.mjs → RecentActivityWidget-D1AlZgfV.mjs} +2 -2
- package/dist/{RecentActivityWidget-DelPdiwR.mjs.map → RecentActivityWidget-D1AlZgfV.mjs.map} +1 -1
- package/dist/{RecentActivityWidget-BNW9aFT4.cjs → RecentActivityWidget-DNyhUZNs.cjs} +2 -2
- package/dist/{RecentActivityWidget-BNW9aFT4.cjs.map → RecentActivityWidget-DNyhUZNs.cjs.map} +1 -1
- package/dist/{ScreenRenderer-ClYgfQf_.mjs → ScreenRenderer-Cl2aAJ7D.mjs} +2 -2
- package/dist/{ScreenRenderer-ClYgfQf_.mjs.map → ScreenRenderer-Cl2aAJ7D.mjs.map} +1 -1
- package/dist/{ScreenRenderer-Ct1w4PNu.cjs → ScreenRenderer-xH01YkEQ.cjs} +2 -2
- package/dist/{ScreenRenderer-Ct1w4PNu.cjs.map → ScreenRenderer-xH01YkEQ.cjs.map} +1 -1
- package/dist/{ShopScreen-DWLGH2gt.cjs → ShopScreen-1yvcCk7l.cjs} +6 -2
- package/dist/ShopScreen-1yvcCk7l.cjs.map +1 -0
- package/dist/{ShopScreen-CZ_290EP.cjs → ShopScreen-6nEKrNjK.cjs} +14 -14
- package/dist/{ShopScreen-BBfOte5o.mjs → ShopScreen-U7G6Jf67.mjs} +6 -2
- package/dist/ShopScreen-U7G6Jf67.mjs.map +1 -0
- package/dist/{ToDoWidget-CYDsZA0Z.mjs → ToDoWidget-BciI_D70.mjs} +2 -2
- package/dist/{ToDoWidget-CYDsZA0Z.mjs.map → ToDoWidget-BciI_D70.mjs.map} +1 -1
- package/dist/{ToDoWidget-Bjoan2Rm.cjs → ToDoWidget-OxT9z59F.cjs} +2 -2
- package/dist/{ToDoWidget-Bjoan2Rm.cjs.map → ToDoWidget-OxT9z59F.cjs.map} +1 -1
- package/dist/{ToDoWidget-C_CvWdLi.cjs → ToDoWidget-mSGQgnu3.cjs} +2 -2
- package/dist/index.cjs +22 -22
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +22 -22
- package/dist/{registry-context-BahYMRqn.mjs → registry-context-CTHUCfEc.mjs} +6 -5
- package/dist/{registry-context-bf52ZIJX.cjs.map → registry-context-CTHUCfEc.mjs.map} +1 -1
- package/dist/{registry-context-bf52ZIJX.cjs → registry-context-CcZYS15Q.cjs} +6 -5
- package/dist/{registry-context-BahYMRqn.mjs.map → registry-context-CcZYS15Q.cjs.map} +1 -1
- package/package.json +10 -10
- package/dist/CarouselWidget-CiKlooUF.cjs.map +0 -1
- package/dist/CarouselWidget-p8Z6L-G5.mjs.map +0 -1
- package/dist/ContainerWidget-CyfPYEAv.cjs +0 -8
- package/dist/PointsWidget-C2KB4k48.mjs.map +0 -1
- package/dist/PointsWidget-DLp-PYus.cjs.map +0 -1
- package/dist/ShopScreen-BBfOte5o.mjs.map +0 -1
- package/dist/ShopScreen-DWLGH2gt.cjs.map +0 -1
package/dist/{RecentActivityWidget-DelPdiwR.mjs.map → RecentActivityWidget-D1AlZgfV.mjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RecentActivityWidget-DelPdiwR.mjs","names":[],"sources":["../../widgets/src/hooks/use-activities.preview.ts","../../widgets/src/hooks/use-activities.ts","../../widgets/src/widgets/RecentActivityWidget.tsx"],"sourcesContent":["import type { Activity } from \"@fluid-app/portal-core/widgets-api-types\";\n\nconst now = new Date();\n\nfunction minutesAgo(minutes: number): string {\n return new Date(now.getTime() - minutes * 60_000).toISOString();\n}\n\nexport const PREVIEW_DATA: Activity[] = [\n {\n id: 1,\n userName: \"Sarah Johnson\",\n avatarUrl: null,\n activityType: \"Order Placed\",\n targetName: \"Wellness Starter Kit\",\n timestamp: minutesAgo(12),\n slug: \"order_placed\",\n },\n {\n id: 2,\n userName: \"Mike Chen\",\n avatarUrl: null,\n activityType: \"New Lead\",\n targetName: \"Signed up from Instagram link\",\n timestamp: minutesAgo(45),\n slug: \"new_lead\",\n },\n {\n id: 3,\n userName: \"Visitor from Austin, TX\",\n avatarUrl: null,\n activityType: \"Page Views\",\n targetName: \"Viewed product catalog (3 pages)\",\n timestamp: minutesAgo(90),\n slug: \"page_views\",\n },\n {\n id: 4,\n userName: \"Lisa Park\",\n avatarUrl: null,\n activityType: \"Video Complete\",\n targetName: \"Watched: Getting Started Guide\",\n timestamp: minutesAgo(180),\n slug: \"video_complete\",\n },\n];\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useWidgetsApi } from \"@fluid-app/portal-core/widgets-api-context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useDataSourceRegistryConfig } from \"@fluid-app/portal-react/data-sources/registry-context\";\nimport { PREVIEW_DATA } from \"./use-activities.preview\";\nimport type { Activity } from \"@fluid-app/portal-core/widgets-api-types\";\n\nexport type {\n Activity,\n ActivitySlug,\n} from \"@fluid-app/portal-core/widgets-api-types\";\n\nexport function useActivities(): UseQueryResult<Activity[], Error> {\n const widgetsApi = useWidgetsApi();\n const { isPreview } = useWidgetPreviewContext();\n const { baseUrl } = useDataSourceRegistryConfig();\n\n return useQuery({\n queryKey: [\n \"portal-widget-use\",\n \"activities\",\n isPreview ? \"preview\" : baseUrl,\n ] as const,\n queryFn: ({ signal }) => widgetsApi.fetchActivities(signal),\n enabled: !isPreview,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\n });\n}\n","import { useEffect, useMemo, useState, type ComponentProps } from \"react\";\nimport type React from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n BorderWidthOptions,\n ColorOptions,\n FontSizeOptions,\n PaddingOptions,\n} from \"@fluid-app/portal-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport {\n borderColorClasses,\n borderWidthClasses,\n getBorderColorField,\n getBorderRadiusField,\n getBorderWidthField,\n getColorField,\n getFontSizeField,\n getPaddingField,\n} from \"../core/fields\";\nimport {\n useActivities,\n type Activity,\n type ActivitySlug,\n} from \"../hooks/use-activities\";\nimport { ErrorState } from \"../components/error-state\";\nimport {\n AudioWaveform,\n Bell,\n Calendar,\n CheckSquare,\n Eye,\n MessageCircle,\n Play,\n ShoppingCart,\n Star,\n Trophy,\n User,\n UserPlus,\n type LucideIcon,\n} from \"lucide-react\";\n\n// ---------- helpers ----------\n\nconst RELATIVE_TIME_REFRESH_MS = 60_000;\nconst DEFAULT_MAX_ITEMS_TO_SHOW = 6;\nconst MAX_ITEMS_TO_SHOW_LIMIT = 20;\n\nconst ACTIVITY_ICON_MAP: Record<ActivitySlug, LucideIcon> = {\n order_placed: ShoppingCart,\n abandoned_cart: ShoppingCart,\n cart_items_added: ShoppingCart,\n new_cart_items_added: ShoppingCart,\n direct_message: MessageCircle,\n comment_reply: MessageCircle,\n message_received: MessageCircle,\n message_sent: MessageCircle,\n video: Play,\n video_complete: Play,\n video_contact: Play,\n video_complete_contact: Play,\n new_lead: UserPlus,\n page_views_contact: UserPlus,\n smart_link_clicked: UserPlus,\n page_views: Eye,\n upcoming_event: Calendar,\n review_left: Star,\n tasks: CheckSquare,\n announcements: Bell,\n fantasy_point: Trophy,\n};\n\nconst getActivityIcon = (slug: ActivitySlug): LucideIcon =>\n ACTIVITY_ICON_MAP[slug] ?? User;\n\nconst formatAbsoluteTime = (timestamp: string): string =>\n new Date(timestamp).toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true,\n });\n\nconst formatRelativeTime = (timestamp: string, now: number): string => {\n const then = new Date(timestamp).getTime();\n const diffSec = Math.max(0, Math.round((now - then) / 1000));\n\n if (diffSec < 60) return \"now\";\n const diffMin = Math.floor(diffSec / 60);\n if (diffMin < 60) return `${diffMin}m`;\n const diffHr = Math.floor(diffMin / 60);\n if (diffHr < 24) return `${diffHr}h`;\n const diffDay = Math.floor(diffHr / 24);\n if (diffDay < 7) return `${diffDay}d`;\n return new Date(timestamp).toLocaleDateString(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n });\n};\n\nconst toLocalDateKey = (date: Date): string => {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n return `${year}-${month}-${day}`;\n};\n\nconst formatDateHeader = (timestamp: string, nowMs: number): string => {\n const date = new Date(timestamp);\n if (!Number.isFinite(date.getTime())) return timestamp;\n\n const now = new Date(nowMs);\n const yesterday = new Date(now);\n yesterday.setDate(now.getDate() - 1);\n\n const todayKey = toLocalDateKey(now);\n const yesterdayKey = toLocalDateKey(yesterday);\n const dateKey = toLocalDateKey(date);\n\n if (dateKey === todayKey) return \"Today\";\n if (dateKey === yesterdayKey) return \"Yesterday\";\n return date.toLocaleDateString(\"en-US\", {\n month: \"long\",\n day: \"numeric\",\n });\n};\n\nconst getDateKey = (timestamp: string): string => {\n const date = new Date(timestamp);\n if (!Number.isFinite(date.getTime())) return timestamp;\n return toLocalDateKey(date);\n};\n\nconst getColorCssValue = (color: ColorOptions): string =>\n color === \"transparent\" ? \"transparent\" : `var(--color-${color})`;\n\nconst normalizeMaxItemsToShow = (value: number): number => {\n if (!Number.isFinite(value)) return DEFAULT_MAX_ITEMS_TO_SHOW;\n return Math.min(MAX_ITEMS_TO_SHOW_LIMIT, Math.max(1, Math.floor(value)));\n};\n\nconst shouldRenderEyebrow = (\n showEyebrow: boolean,\n titleText: string,\n): boolean =>\n showEyebrow && titleText.trim().toLowerCase() !== \"recent activity\";\n\nconst isUnknownUser = (name: string | undefined): boolean => {\n if (!name) return true;\n const lower = name.trim().toLowerCase();\n if (!lower) return true;\n return (\n lower === \"unknown\" ||\n lower.includes(\"unknown visitor\") ||\n lower === \"anonymous\" ||\n lower === \"guest\"\n );\n};\n\nconst getInitials = (name: string | undefined): string => {\n if (!name) return \"\";\n const parts = name.trim().split(/\\s+/).slice(0, 2);\n return parts.map((p) => p[0]?.toUpperCase() ?? \"\").join(\"\");\n};\n\n// ---------- avatar ----------\n\ntype AvatarProps = {\n activity: Activity;\n textColor: ColorOptions;\n accentColor: ColorOptions;\n surfaceColor: ColorOptions;\n showTypeBadge: boolean;\n};\n\nfunction Avatar({\n activity,\n textColor,\n accentColor,\n surfaceColor,\n showTypeBadge,\n}: AvatarProps) {\n const TypeIcon = getActivityIcon(activity.slug);\n const unknown = isUnknownUser(activity.userName);\n const initials = unknown ? \"\" : getInitials(activity.userName);\n // 3-tier fallback: image → initials → person icon. If avatarUrl 404s or\n // fails to decode, flip to the next tier instead of showing a broken image.\n const [failedAvatarUrl, setFailedAvatarUrl] = useState<string | null>(null);\n const avatarUrl = activity.avatarUrl;\n const showImage = Boolean(avatarUrl) && failedAvatarUrl !== avatarUrl;\n\n return (\n <div className=\"relative shrink-0\">\n {showImage ? (\n <img\n src={avatarUrl ?? undefined}\n alt={unknown ? \"\" : activity.userName}\n width={36}\n height={36}\n loading=\"lazy\"\n onError={() => setFailedAvatarUrl(avatarUrl)}\n className=\"size-9 rounded-full object-cover\"\n style={{\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n />\n ) : initials ? (\n <div\n className={`flex size-9 items-center justify-center rounded-full text-[11px] font-bold tracking-[0.04em] text-${textColor}/80`}\n style={{\n background: `color-mix(in oklch, var(--color-${accentColor}) 12%, transparent)`,\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n aria-label={activity.userName}\n >\n {initials}\n </div>\n ) : (\n <div\n className={`flex size-9 items-center justify-center rounded-full`}\n style={{\n background: `color-mix(in oklch, var(--color-${textColor}) 5%, transparent)`,\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n aria-label=\"Unknown visitor\"\n >\n <User className={`size-3.5 text-${textColor}/55`} />\n </div>\n )}\n\n {/* Type badge — shows what kind of event, regardless of avatar kind. */}\n {showTypeBadge && (\n <span\n className={`bg-${accentColor} text-${accentColor}-foreground absolute -right-0.5 -bottom-0.5 flex size-4 items-center justify-center rounded-full`}\n style={{\n boxShadow: `0 0 0 2px ${getColorCssValue(surfaceColor)}`,\n }}\n aria-hidden=\"true\"\n >\n <TypeIcon style={{ width: 8, height: 8 }} />\n </span>\n )}\n </div>\n );\n}\n\n// ---------- activity row ----------\n\ntype ActivityRowProps = {\n activity: Activity;\n accentColor: ColorOptions;\n textColor: ColorOptions;\n surfaceColor: ColorOptions;\n showRelativeTime: boolean;\n nowMs: number;\n};\n\nfunction ActivityRow({\n activity,\n accentColor,\n textColor,\n surfaceColor,\n showRelativeTime,\n nowMs,\n}: ActivityRowProps) {\n const timeText = showRelativeTime\n ? formatRelativeTime(activity.timestamp, nowMs)\n : formatAbsoluteTime(activity.timestamp);\n\n const absoluteTooltip = new Date(activity.timestamp).toLocaleString(\"en-US\", {\n weekday: \"short\",\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n\n return (\n <li\n role=\"article\"\n className=\"relative flex items-start gap-3 rounded-md px-2 py-2\"\n >\n <Avatar\n activity={activity}\n textColor={textColor}\n accentColor={accentColor}\n surfaceColor={surfaceColor}\n showTypeBadge\n />\n\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <div className=\"flex items-baseline gap-2\">\n <p\n className={`min-w-0 flex-1 truncate text-[13px] leading-tight font-semibold text-${textColor}`}\n >\n {activity.activityType}\n </p>\n <span\n title={absoluteTooltip}\n className={`shrink-0 font-mono text-[10px] font-medium tabular-nums text-${textColor}/45`}\n >\n {timeText}\n </span>\n </div>\n <p\n className={`mt-0.5 truncate text-[12px] leading-snug text-${textColor}/65`}\n >\n <span className={`font-semibold text-${textColor}/85`}>\n {activity.userName}\n </span>\n {activity.targetName && (\n <>\n <span className={`mx-1 text-${textColor}/35`}>·</span>\n <span>{activity.targetName}</span>\n </>\n )}\n </p>\n </div>\n </li>\n );\n}\n\n// ---------- widget ----------\n\ntype RecentActivityWidgetProps = ComponentProps<\"div\"> & {\n titleEnabled?: boolean;\n titleText?: string;\n titleFontSize?: FontSizeOptions;\n titleColor?: ColorOptions;\n\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n borderWidth?: BorderWidthOptions;\n borderColor?: ColorOptions;\n\n maxItemsToShow?: number;\n\n // Polish (additive)\n showTimeline?: boolean;\n showRelativeTime?: boolean;\n showCountChip?: boolean;\n showEyebrow?: boolean;\n};\n\nfunction useRelativeTimeNow(): number {\n const [nowMs, setNowMs] = useState(() => Date.now());\n\n useEffect(() => {\n const timer = window.setInterval(() => {\n setNowMs(Date.now());\n }, RELATIVE_TIME_REFRESH_MS);\n\n return () => window.clearInterval(timer);\n }, []);\n\n return nowMs;\n}\n\nexport function RecentActivityWidget({\n titleEnabled = true,\n titleText = \"Activity\",\n titleFontSize = \"xl\",\n titleColor = \"foreground\",\n\n background = { type: \"solid\", color: \"background\" },\n textColor = \"foreground\",\n accentColor = \"primary\",\n padding = 4,\n borderRadius = \"md\",\n borderWidth = \"thin\",\n borderColor = \"muted\",\n\n maxItemsToShow = 6,\n\n showTimeline = true,\n showRelativeTime = true,\n showCountChip = true,\n showEyebrow = true,\n\n className,\n ...props\n}: RecentActivityWidgetProps): React.JSX.Element {\n const backgroundColor = background.color || \"background\";\n const backgroundImage =\n (background.resource?.image_url || background.resource?.imageUrl) &&\n background.type === \"image\"\n ? `url(${background.resource.image_url || background.resource.imageUrl})`\n : \"none\";\n\n const { data: activities = [], isLoading, isError } = useActivities();\n const nowMs = useRelativeTimeNow();\n const maxVisibleItems = normalizeMaxItemsToShow(maxItemsToShow);\n\n const activitiesToShow = useMemo(() => {\n if (activities.length === 0)\n return [] as { date: string; items: Activity[] }[];\n const groups = new Map<string, Activity[]>();\n for (const activity of activities.slice(0, maxVisibleItems)) {\n const key = getDateKey(activity.timestamp);\n const existing = groups.get(key);\n if (existing) existing.push(activity);\n else groups.set(key, [activity]);\n }\n return Array.from(groups.entries()).map(([key, items]) => ({\n // `key` is only a defensive fallback; every group is created with at\n // least one activity, so the item timestamp is what normally labels it.\n date: formatDateHeader(items[0]?.timestamp ?? key, nowMs),\n items,\n }));\n }, [activities, maxVisibleItems, nowMs]);\n\n const totalCount = Math.min(activities.length, maxVisibleItems);\n\n return (\n <div\n className={`@container overflow-hidden rounded-${borderRadius} bg-${backgroundColor} text-${textColor} ${borderWidthClasses[borderWidth]} ${borderWidth !== \"none\" ? borderColorClasses[borderColor] : \"\"} ${className ?? \"\"}`}\n style={{\n backgroundImage,\n // Soft ambient lift derived from theme tokens. The configured border\n // props own the crisp outline so saved border settings stay intact.\n boxShadow: `0 1px 2px color-mix(in oklch, var(--color-foreground) 4%, transparent), 0 14px 32px -20px color-mix(in oklch, var(--color-foreground) 14%, transparent)`,\n }}\n {...props}\n >\n <div className={`p-${padding} flex flex-col`}>\n {/* Header */}\n {titleEnabled && titleText && (\n <div className=\"mb-4 flex items-end justify-between gap-3\">\n <div className=\"flex min-w-0 flex-col\">\n {shouldRenderEyebrow(showEyebrow, titleText) && (\n <span\n className={`text-[10px] font-bold tracking-[0.2em] uppercase text-${textColor}/55`}\n >\n Recent\n </span>\n )}\n <h2\n className={`mt-0.5 text-${titleFontSize} leading-tight font-bold tracking-[-0.015em] text-${titleColor}`}\n >\n {titleText}\n </h2>\n </div>\n {!isLoading && !isError && showCountChip && totalCount > 0 && (\n <span\n className={`inline-flex shrink-0 items-center gap-1.5 rounded-full bg-${accentColor}/10 px-2 py-1 text-[10px] font-bold tracking-[0.06em] text-${accentColor}`}\n >\n <span\n aria-hidden=\"true\"\n className={`size-1.5 rounded-full bg-${accentColor}`}\n />\n {totalCount} {totalCount === 1 ? \"update\" : \"updates\"}\n </span>\n )}\n </div>\n )}\n\n {/* States */}\n {isLoading ? (\n <ActivitySkeleton\n textColor={textColor}\n showTimeline={showTimeline}\n rowCount={maxVisibleItems}\n />\n ) : isError ? (\n <ErrorState />\n ) : activities.length === 0 ? (\n <EmptyState textColor={textColor} accentColor={accentColor} />\n ) : (\n /* Feed */\n <div role=\"feed\" className=\"flex flex-col gap-3\">\n {activitiesToShow.map((group, groupIdx) => (\n <section\n key={`${group.date}-${groupIdx}`}\n className=\"flex flex-col gap-1\"\n >\n <header className=\"flex items-center gap-2 px-2\">\n <span\n className={`text-[10px] font-bold tracking-[0.18em] uppercase text-${textColor}/55`}\n >\n {group.date}\n </span>\n <span\n aria-hidden=\"true\"\n className=\"h-px flex-1\"\n style={{\n background: `color-mix(in oklch, var(--color-${textColor}) 8%, transparent)`,\n }}\n />\n </header>\n\n <ul className=\"relative flex flex-col\">\n {/* Single continuous spine — anchored to the first and last\n avatar centers. Avatar is size-9 (36px), inside a li with\n px-2 py-2 (8px padding). So avatar center = 8 + 18 = 26px\n from the edges, which is also where the spine lives. */}\n {showTimeline && group.items.length > 1 && (\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute top-[26px] bottom-[26px] left-[26px] w-px\"\n style={{\n background: `color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n />\n )}\n {group.items.map((activity) => (\n <ActivityRow\n key={activity.id}\n activity={activity}\n accentColor={accentColor}\n textColor={textColor}\n surfaceColor={backgroundColor}\n showRelativeTime={showRelativeTime}\n nowMs={nowMs}\n />\n ))}\n </ul>\n </section>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n\n// ---------- empty state ----------\n\ntype EmptyStateProps = {\n textColor: ColorOptions;\n accentColor: ColorOptions;\n};\n\nfunction EmptyState({ textColor, accentColor }: EmptyStateProps) {\n return (\n <div className=\"flex flex-col items-center justify-center gap-3 px-4 py-10\">\n <div\n className=\"relative flex size-12 items-center justify-center rounded-full\"\n style={{\n background: `color-mix(in oklch, var(--color-${accentColor}) 10%, transparent)`,\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${textColor}) 8%, transparent)`,\n }}\n >\n <AudioWaveform className={`size-5 text-${accentColor}/80`} />\n </div>\n <div className=\"flex flex-col items-center gap-1\">\n <p className={`text-[13px] font-semibold text-${textColor}/80`}>\n No activity yet\n </p>\n <p\n className={`max-w-[24ch] text-center text-[11px] leading-snug text-${textColor}/55`}\n >\n Orders, messages, views and leads will show up here as they happen.\n </p>\n </div>\n </div>\n );\n}\n\n// ---------- skeleton ----------\n\ntype ActivitySkeletonProps = {\n textColor: ColorOptions;\n showTimeline: boolean;\n rowCount: number;\n};\n\nfunction ActivitySkeleton({\n textColor,\n showTimeline,\n rowCount,\n}: ActivitySkeletonProps) {\n return (\n <div className=\"relative flex flex-col\" aria-busy=\"true\">\n {showTimeline && (\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute top-[26px] bottom-[26px] left-[26px] w-px\"\n style={{\n background: `color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n />\n )}\n {Array.from({ length: rowCount }, (_, i) => i).map((i) => (\n <div\n key={i}\n className=\"relative flex items-start gap-3 rounded-md px-2 py-2\"\n >\n <div\n className={`size-9 shrink-0 animate-pulse rounded-full bg-${textColor}/10`}\n />\n <div className=\"flex min-w-0 flex-1 flex-col gap-1.5\">\n <div\n className={`h-2 w-2/3 animate-pulse rounded-full bg-${textColor}/10`}\n />\n <div\n className={`h-2 w-1/2 animate-pulse rounded-full bg-${textColor}/10`}\n />\n </div>\n </div>\n ))}\n </div>\n );\n}\n\n// ---------- schema ----------\n\nexport const recentActivityWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"RecentActivityWidget\",\n displayName: \"Recent Activity Widget\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [\n // Title\n {\n key: \"titleEnabled\",\n label: \"Widget Title\",\n type: \"boolean\",\n description: \"Display the title and eyebrow above the feed\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n },\n {\n key: \"titleText\",\n label: \"Title\",\n type: \"text\",\n description: \"Title text displayed above the activity feed\",\n defaultValue: \"Activity\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n getFontSizeField({\n key: \"titleFontSize\",\n label: \"Title Font Size\",\n description: \"Font size for the widget title\",\n defaultValue: \"xl\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n getColorField({\n key: \"titleColor\",\n label: \"Title Color\",\n description: \"Color for the widget title\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n {\n key: \"showEyebrow\",\n label: \"Show 'RECENT' Eyebrow\",\n type: \"boolean\",\n description: \"Small uppercase label above the title\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n {\n key: \"showCountChip\",\n label: \"Show Updates Count Chip\",\n type: \"boolean\",\n description: \"Accent pill next to the title showing how many updates\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n\n // Feed\n {\n key: \"maxItemsToShow\",\n label: \"Max Items\",\n type: \"number\",\n description: \"Maximum number of activity items to display\",\n defaultValue: DEFAULT_MAX_ITEMS_TO_SHOW,\n min: 1,\n max: MAX_ITEMS_TO_SHOW_LIMIT,\n step: 1,\n tab: \"styling\",\n group: \"Feed\",\n },\n {\n key: \"showTimeline\",\n label: \"Timeline Spine\",\n type: \"boolean\",\n description: \"Draw a vertical line connecting activity avatars\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Feed\",\n },\n {\n key: \"showRelativeTime\",\n label: \"Relative Timestamps\",\n type: \"boolean\",\n description: \"Show 'now / 2m / 1h / 3d' instead of absolute times\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Feed\",\n },\n\n // Design\n {\n type: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Background for the widget container\",\n defaultValue: \"background\",\n tab: \"styling\",\n group: \"Design\",\n },\n getColorField({\n key: \"textColor\",\n label: \"Text Color\",\n description: \"Default text color for activity content\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getColorField({\n key: \"accentColor\",\n label: \"Accent Color\",\n description:\n \"Drives the count chip, type badge, initials avatar, and empty-state icon\",\n defaultValue: \"primary\",\n tab: \"styling\",\n group: \"Design\",\n }),\n {\n key: \"separator\",\n type: \"separator\",\n label: \"Separator\",\n tab: \"styling\",\n group: \"Design\",\n },\n getPaddingField({\n key: \"padding\",\n label: \"Padding\",\n description: \"Padding around the widget container\",\n defaultValue: 4,\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderRadiusField({\n key: \"borderRadius\",\n label: \"Border Radius\",\n description: \"Border radius for the widget container\",\n defaultValue: \"md\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderWidthField({\n key: \"borderWidth\",\n label: \"Border Width\",\n description: \"Border width for the widget container\",\n defaultValue: \"thin\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderColorField({\n key: \"borderColor\",\n label: \"Border Color\",\n description: \"Border color for the widget container\",\n defaultValue: \"muted\",\n tab: \"styling\",\n group: \"Design\",\n }),\n ],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":";;;;;;;;;AAEA,MAAM,sBAAM,IAAI,MAAM;AAEtB,SAAS,WAAW,SAAyB;AAC3C,yBAAO,IAAI,KAAK,IAAI,SAAS,GAAG,UAAU,IAAO,EAAC,aAAa;;AAGjE,MAAa,eAA2B;CACtC;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAW,WAAW,GAAG;EACzB,MAAM;EACP;CACD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAW,WAAW,GAAG;EACzB,MAAM;EACP;CACD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAW,WAAW,GAAG;EACzB,MAAM;EACP;CACD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAW,WAAW,IAAI;EAC1B,MAAM;EACP;CACF;;;ACjCD,SAAgB,gBAAmD;CACjE,MAAM,aAAa,eAAe;CAClC,MAAM,EAAE,cAAc,yBAAyB;CAC/C,MAAM,EAAE,YAAY,6BAA6B;AAEjD,QAAO,SAAS;EACd,UAAU;GACR;GACA;GACA,YAAY,YAAY;GACzB;EACD,UAAU,EAAE,aAAa,WAAW,gBAAgB,OAAO;EAC3D,SAAS,CAAC;EACV,GAAI,aAAa,EAAE,iBAAiB,cAAc;EACnD,CAAC;;;;;;;;ACmBJ,MAAM,2BAA2B;AACjC,MAAM,4BAA4B;AAClC,MAAM,0BAA0B;AAEhC,MAAM,oBAAsD;CAC1D,cAAc;CACd,gBAAgB;CAChB,kBAAkB;CAClB,sBAAsB;CACtB,gBAAgB;CAChB,eAAe;CACf,kBAAkB;CAClB,cAAc;CACd,OAAO;CACP,gBAAgB;CAChB,eAAe;CACf,wBAAwB;CACxB,UAAU;CACV,oBAAoB;CACpB,oBAAoB;CACpB,YAAY;CACZ,gBAAgB;CAChB,aAAa;CACb,OAAO;CACP,eAAe;CACf,eAAe;CAChB;AAED,MAAM,mBAAmB,SACvB,kBAAkB,SAAS;AAE7B,MAAM,sBAAsB,cAC1B,IAAI,KAAK,UAAU,CAAC,mBAAmB,SAAS;CAC9C,MAAM;CACN,QAAQ;CACR,QAAQ;CACT,CAAC;AAEJ,MAAM,sBAAsB,WAAmB,QAAwB;CACrE,MAAM,OAAO,IAAI,KAAK,UAAU,CAAC,SAAS;CAC1C,MAAM,UAAU,KAAK,IAAI,GAAG,KAAK,OAAO,MAAM,QAAQ,IAAK,CAAC;AAE5D,KAAI,UAAU,GAAI,QAAO;CACzB,MAAM,UAAU,KAAK,MAAM,UAAU,GAAG;AACxC,KAAI,UAAU,GAAI,QAAO,GAAG,QAAQ;CACpC,MAAM,SAAS,KAAK,MAAM,UAAU,GAAG;AACvC,KAAI,SAAS,GAAI,QAAO,GAAG,OAAO;CAClC,MAAM,UAAU,KAAK,MAAM,SAAS,GAAG;AACvC,KAAI,UAAU,EAAG,QAAO,GAAG,QAAQ;AACnC,QAAO,IAAI,KAAK,UAAU,CAAC,mBAAmB,SAAS;EACrD,OAAO;EACP,KAAK;EACN,CAAC;;AAGJ,MAAM,kBAAkB,SAAuB;AAI7C,QAAO,GAHM,KAAK,aAAa,CAGhB,GAFD,OAAO,KAAK,UAAU,GAAG,EAAE,CAAC,SAAS,GAAG,IAAI,CAElC,GADZ,OAAO,KAAK,SAAS,CAAC,CAAC,SAAS,GAAG,IAAI;;AAIrD,MAAM,oBAAoB,WAAmB,UAA0B;CACrE,MAAM,OAAO,IAAI,KAAK,UAAU;AAChC,KAAI,CAAC,OAAO,SAAS,KAAK,SAAS,CAAC,CAAE,QAAO;CAE7C,MAAM,MAAM,IAAI,KAAK,MAAM;CAC3B,MAAM,YAAY,IAAI,KAAK,IAAI;AAC/B,WAAU,QAAQ,IAAI,SAAS,GAAG,EAAE;CAEpC,MAAM,WAAW,eAAe,IAAI;CACpC,MAAM,eAAe,eAAe,UAAU;CAC9C,MAAM,UAAU,eAAe,KAAK;AAEpC,KAAI,YAAY,SAAU,QAAO;AACjC,KAAI,YAAY,aAAc,QAAO;AACrC,QAAO,KAAK,mBAAmB,SAAS;EACtC,OAAO;EACP,KAAK;EACN,CAAC;;AAGJ,MAAM,cAAc,cAA8B;CAChD,MAAM,OAAO,IAAI,KAAK,UAAU;AAChC,KAAI,CAAC,OAAO,SAAS,KAAK,SAAS,CAAC,CAAE,QAAO;AAC7C,QAAO,eAAe,KAAK;;AAG7B,MAAM,oBAAoB,UACxB,UAAU,gBAAgB,gBAAgB,eAAe,MAAM;AAEjE,MAAM,2BAA2B,UAA0B;AACzD,KAAI,CAAC,OAAO,SAAS,MAAM,CAAE,QAAO;AACpC,QAAO,KAAK,IAAI,yBAAyB,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,CAAC,CAAC;;AAG1E,MAAM,uBACJ,aACA,cAEA,eAAe,UAAU,MAAM,CAAC,aAAa,KAAK;AAEpD,MAAM,iBAAiB,SAAsC;AAC3D,KAAI,CAAC,KAAM,QAAO;CAClB,MAAM,QAAQ,KAAK,MAAM,CAAC,aAAa;AACvC,KAAI,CAAC,MAAO,QAAO;AACnB,QACE,UAAU,aACV,MAAM,SAAS,kBAAkB,IACjC,UAAU,eACV,UAAU;;AAId,MAAM,eAAe,SAAqC;AACxD,KAAI,CAAC,KAAM,QAAO;AAElB,QADc,KAAK,MAAM,CAAC,MAAM,MAAM,CAAC,MAAM,GAAG,EAAE,CACrC,KAAK,MAAM,EAAE,IAAI,aAAa,IAAI,GAAG,CAAC,KAAK,GAAG;;AAa7D,SAAS,OAAO,EACd,UACA,WACA,aACA,cACA,iBACc;CACd,MAAM,WAAW,gBAAgB,SAAS,KAAK;CAC/C,MAAM,UAAU,cAAc,SAAS,SAAS;CAChD,MAAM,WAAW,UAAU,KAAK,YAAY,SAAS,SAAS;CAG9D,MAAM,CAAC,iBAAiB,sBAAsB,SAAwB,KAAK;CAC3E,MAAM,YAAY,SAAS;AAG3B,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CAHgB,QAAQ,UAAU,IAAI,oBAAoB,YAKtD,oBAAC,OAAD;GACE,KAAK,aAAa,KAAA;GAClB,KAAK,UAAU,KAAK,SAAS;GAC7B,OAAO;GACP,QAAQ;GACR,SAAQ;GACR,eAAe,mBAAmB,UAAU;GAC5C,WAAU;GACV,OAAO,EACL,WAAW,mDAAmD,UAAU,sBACzE;GACD,CAAA,GACA,WACF,oBAAC,OAAD;GACE,WAAW,qGAAqG,UAAU;GAC1H,OAAO;IACL,YAAY,mCAAmC,YAAY;IAC3D,WAAW,mDAAmD,UAAU;IACzE;GACD,cAAY,SAAS;aAEpB;GACG,CAAA,GAEN,oBAAC,OAAD;GACE,WAAW;GACX,OAAO;IACL,YAAY,mCAAmC,UAAU;IACzD,WAAW,mDAAmD,UAAU;IACzE;GACD,cAAW;aAEX,oBAAC,MAAD,EAAM,WAAW,iBAAiB,UAAU,MAAQ,CAAA;GAChD,CAAA,EAIP,iBACC,oBAAC,QAAD;GACE,WAAW,MAAM,YAAY,QAAQ,YAAY;GACjD,OAAO,EACL,WAAW,aAAa,iBAAiB,aAAa,IACvD;GACD,eAAY;aAEZ,oBAAC,UAAD,EAAU,OAAO;IAAE,OAAO;IAAG,QAAQ;IAAG,EAAI,CAAA;GACvC,CAAA,CAEL;;;AAeV,SAAS,YAAY,EACnB,UACA,aACA,WACA,cACA,kBACA,SACmB;CACnB,MAAM,WAAW,mBACb,mBAAmB,SAAS,WAAW,MAAM,GAC7C,mBAAmB,SAAS,UAAU;CAE1C,MAAM,kBAAkB,IAAI,KAAK,SAAS,UAAU,CAAC,eAAe,SAAS;EAC3E,SAAS;EACT,OAAO;EACP,KAAK;EACL,MAAM;EACN,QAAQ;EACT,CAAC;AAEF,QACE,qBAAC,MAAD;EACE,MAAK;EACL,WAAU;YAFZ,CAIE,oBAAC,QAAD;GACY;GACC;GACE;GACC;GACd,eAAA;GACA,CAAA,EAEF,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,KAAD;KACE,WAAW,wEAAwE;eAElF,SAAS;KACR,CAAA,EACJ,oBAAC,QAAD;KACE,OAAO;KACP,WAAW,gEAAgE,UAAU;eAEpF;KACI,CAAA,CACH;OACN,qBAAC,KAAD;IACE,WAAW,iDAAiD,UAAU;cADxE,CAGE,oBAAC,QAAD;KAAM,WAAW,sBAAsB,UAAU;eAC9C,SAAS;KACL,CAAA,EACN,SAAS,cACR,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,QAAD;KAAM,WAAW,aAAa,UAAU;eAAM;KAAQ,CAAA,EACtD,oBAAC,QAAD,EAAA,UAAO,SAAS,YAAkB,CAAA,CACjC,EAAA,CAAA,CAEH;MACA;KACH;;;AA6BT,SAAS,qBAA6B;CACpC,MAAM,CAAC,OAAO,YAAY,eAAe,KAAK,KAAK,CAAC;AAEpD,iBAAgB;EACd,MAAM,QAAQ,OAAO,kBAAkB;AACrC,YAAS,KAAK,KAAK,CAAC;KACnB,yBAAyB;AAE5B,eAAa,OAAO,cAAc,MAAM;IACvC,EAAE,CAAC;AAEN,QAAO;;AAGT,SAAgB,qBAAqB,EACnC,eAAe,MACf,YAAY,YACZ,gBAAgB,MAChB,aAAa,cAEb,aAAa;CAAE,MAAM;CAAS,OAAO;CAAc,EACnD,YAAY,cACZ,cAAc,WACd,UAAU,GACV,eAAe,MACf,cAAc,QACd,cAAc,SAEd,iBAAiB,GAEjB,eAAe,MACf,mBAAmB,MACnB,gBAAgB,MAChB,cAAc,MAEd,WACA,GAAG,SAC4C;CAC/C,MAAM,kBAAkB,WAAW,SAAS;CAC5C,MAAM,mBACH,WAAW,UAAU,aAAa,WAAW,UAAU,aACxD,WAAW,SAAS,UAChB,OAAO,WAAW,SAAS,aAAa,WAAW,SAAS,SAAS,KACrE;CAEN,MAAM,EAAE,MAAM,aAAa,EAAE,EAAE,WAAW,YAAY,eAAe;CACrE,MAAM,QAAQ,oBAAoB;CAClC,MAAM,kBAAkB,wBAAwB,eAAe;CAE/D,MAAM,mBAAmB,cAAc;AACrC,MAAI,WAAW,WAAW,EACxB,QAAO,EAAE;EACX,MAAM,yBAAS,IAAI,KAAyB;AAC5C,OAAK,MAAM,YAAY,WAAW,MAAM,GAAG,gBAAgB,EAAE;GAC3D,MAAM,MAAM,WAAW,SAAS,UAAU;GAC1C,MAAM,WAAW,OAAO,IAAI,IAAI;AAChC,OAAI,SAAU,UAAS,KAAK,SAAS;OAChC,QAAO,IAAI,KAAK,CAAC,SAAS,CAAC;;AAElC,SAAO,MAAM,KAAK,OAAO,SAAS,CAAC,CAAC,KAAK,CAAC,KAAK,YAAY;GAGzD,MAAM,iBAAiB,MAAM,IAAI,aAAa,KAAK,MAAM;GACzD;GACD,EAAE;IACF;EAAC;EAAY;EAAiB;EAAM,CAAC;CAExC,MAAM,aAAa,KAAK,IAAI,WAAW,QAAQ,gBAAgB;AAE/D,QACE,oBAAC,OAAD;EACE,WAAW,sCAAsC,aAAa,MAAM,gBAAgB,QAAQ,UAAU,GAAG,mBAAmB,aAAa,GAAG,gBAAgB,SAAS,mBAAmB,eAAe,GAAG,GAAG,aAAa;EAC1N,OAAO;GACL;GAGA,WAAW;GACZ;EACD,GAAI;YAEJ,qBAAC,OAAD;GAAK,WAAW,KAAK,QAAQ;aAA7B,CAEG,gBAAgB,aACf,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,qBAAC,OAAD;KAAK,WAAU;eAAf,CACG,oBAAoB,aAAa,UAAU,IAC1C,oBAAC,QAAD;MACE,WAAW,yDAAyD,UAAU;gBAC/E;MAEM,CAAA,EAET,oBAAC,MAAD;MACE,WAAW,eAAe,cAAc,oDAAoD;gBAE3F;MACE,CAAA,CACD;QACL,CAAC,aAAa,CAAC,WAAW,iBAAiB,aAAa,KACvD,qBAAC,QAAD;KACE,WAAW,6DAA6D,YAAY,6DAA6D;eADnJ;MAGE,oBAAC,QAAD;OACE,eAAY;OACZ,WAAW,4BAA4B;OACvC,CAAA;MACD;MAAW;MAAE,eAAe,IAAI,WAAW;MACvC;OAEL;OAIP,YACC,oBAAC,kBAAD;IACa;IACG;IACd,UAAU;IACV,CAAA,GACA,UACF,oBAAC,YAAD,EAAc,CAAA,GACZ,WAAW,WAAW,IACxB,oBAAC,YAAD;IAAuB;IAAwB;IAAe,CAAA,GAG9D,oBAAC,OAAD;IAAK,MAAK;IAAO,WAAU;cACxB,iBAAiB,KAAK,OAAO,aAC5B,qBAAC,WAAD;KAEE,WAAU;eAFZ,CAIE,qBAAC,UAAD;MAAQ,WAAU;gBAAlB,CACE,oBAAC,QAAD;OACE,WAAW,0DAA0D,UAAU;iBAE9E,MAAM;OACF,CAAA,EACP,oBAAC,QAAD;OACE,eAAY;OACZ,WAAU;OACV,OAAO,EACL,YAAY,mCAAmC,UAAU,qBAC1D;OACD,CAAA,CACK;SAET,qBAAC,MAAD;MAAI,WAAU;gBAAd,CAKG,gBAAgB,MAAM,MAAM,SAAS,KACpC,oBAAC,QAAD;OACE,eAAY;OACZ,WAAU;OACV,OAAO,EACL,YAAY,mCAAmC,UAAU,sBAC1D;OACD,CAAA,EAEH,MAAM,MAAM,KAAK,aAChB,oBAAC,aAAD;OAEY;OACG;OACF;OACX,cAAc;OACI;OACX;OACP,EAPK,SAAS,GAOd,CACF,CACC;QACG;OA5CH,GAAG,MAAM,KAAK,GAAG,WA4Cd,CACV;IACE,CAAA,CAEJ;;EACF,CAAA;;AAWV,SAAS,WAAW,EAAE,WAAW,eAAgC;AAC/D,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAAC,OAAD;GACE,WAAU;GACV,OAAO;IACL,YAAY,mCAAmC,YAAY;IAC3D,WAAW,mDAAmD,UAAU;IACzE;aAED,oBAAC,eAAD,EAAe,WAAW,eAAe,YAAY,MAAQ,CAAA;GACzD,CAAA,EACN,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,KAAD;IAAG,WAAW,kCAAkC,UAAU;cAAM;IAE5D,CAAA,EACJ,oBAAC,KAAD;IACE,WAAW,0DAA0D,UAAU;cAChF;IAEG,CAAA,CACA;KACF;;;AAYV,SAAS,iBAAiB,EACxB,WACA,cACA,YACwB;AACxB,QACE,qBAAC,OAAD;EAAK,WAAU;EAAyB,aAAU;YAAlD,CACG,gBACC,oBAAC,QAAD;GACE,eAAY;GACZ,WAAU;GACV,OAAO,EACL,YAAY,mCAAmC,UAAU,sBAC1D;GACD,CAAA,EAEH,MAAM,KAAK,EAAE,QAAQ,UAAU,GAAG,GAAG,MAAM,EAAE,CAAC,KAAK,MAClD,qBAAC,OAAD;GAEE,WAAU;aAFZ,CAIE,oBAAC,OAAD,EACE,WAAW,iDAAiD,UAAU,MACtE,CAAA,EACF,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,OAAD,EACE,WAAW,2CAA2C,UAAU,MAChE,CAAA,EACF,oBAAC,OAAD,EACE,WAAW,2CAA2C,UAAU,MAChE,CAAA,CACE;MACF;KAdC,EAcD,CACN,CACE;;;AAMV,MAAa,qCAA2D;CACtE,YAAY;CACZ,aAAa;CACb,YAAY,CAAC;EAAE,IAAI;EAAW,OAAO;EAAW,CAAC;CACjD,QAAQ;EAEN;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EACD,iBAAiB;GACf,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EACF,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EACF;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EAGD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,KAAK;GACL,MAAM;GACN,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EAGD;GACE,MAAM;GACN,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aACE;GACF,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF;GACE,KAAK;GACL,MAAM;GACN,OAAO;GACP,KAAK;GACL,OAAO;GACR;EACD,gBAAgB;GACd,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,qBAAqB;GACnB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,oBAAoB;GAClB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,oBAAoB;GAClB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACH;CACF"}
|
|
1
|
+
{"version":3,"file":"RecentActivityWidget-D1AlZgfV.mjs","names":[],"sources":["../../widgets/src/hooks/use-activities.preview.ts","../../widgets/src/hooks/use-activities.ts","../../widgets/src/widgets/RecentActivityWidget.tsx"],"sourcesContent":["import type { Activity } from \"@fluid-app/portal-core/widgets-api-types\";\n\nconst now = new Date();\n\nfunction minutesAgo(minutes: number): string {\n return new Date(now.getTime() - minutes * 60_000).toISOString();\n}\n\nexport const PREVIEW_DATA: Activity[] = [\n {\n id: 1,\n userName: \"Sarah Johnson\",\n avatarUrl: null,\n activityType: \"Order Placed\",\n targetName: \"Wellness Starter Kit\",\n timestamp: minutesAgo(12),\n slug: \"order_placed\",\n },\n {\n id: 2,\n userName: \"Mike Chen\",\n avatarUrl: null,\n activityType: \"New Lead\",\n targetName: \"Signed up from Instagram link\",\n timestamp: minutesAgo(45),\n slug: \"new_lead\",\n },\n {\n id: 3,\n userName: \"Visitor from Austin, TX\",\n avatarUrl: null,\n activityType: \"Page Views\",\n targetName: \"Viewed product catalog (3 pages)\",\n timestamp: minutesAgo(90),\n slug: \"page_views\",\n },\n {\n id: 4,\n userName: \"Lisa Park\",\n avatarUrl: null,\n activityType: \"Video Complete\",\n targetName: \"Watched: Getting Started Guide\",\n timestamp: minutesAgo(180),\n slug: \"video_complete\",\n },\n];\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useWidgetsApi } from \"@fluid-app/portal-core/widgets-api-context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useDataSourceRegistryConfig } from \"@fluid-app/portal-react/data-sources/registry-context\";\nimport { PREVIEW_DATA } from \"./use-activities.preview\";\nimport type { Activity } from \"@fluid-app/portal-core/widgets-api-types\";\n\nexport type {\n Activity,\n ActivitySlug,\n} from \"@fluid-app/portal-core/widgets-api-types\";\n\nexport function useActivities(): UseQueryResult<Activity[], Error> {\n const widgetsApi = useWidgetsApi();\n const { isPreview } = useWidgetPreviewContext();\n const { baseUrl } = useDataSourceRegistryConfig();\n\n return useQuery({\n queryKey: [\n \"portal-widget-use\",\n \"activities\",\n isPreview ? \"preview\" : baseUrl,\n ] as const,\n queryFn: ({ signal }) => widgetsApi.fetchActivities(signal),\n enabled: !isPreview,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\n });\n}\n","import { useEffect, useMemo, useState, type ComponentProps } from \"react\";\nimport type React from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n BorderWidthOptions,\n ColorOptions,\n FontSizeOptions,\n PaddingOptions,\n} from \"@fluid-app/portal-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport {\n borderColorClasses,\n borderWidthClasses,\n getBorderColorField,\n getBorderRadiusField,\n getBorderWidthField,\n getColorField,\n getFontSizeField,\n getPaddingField,\n} from \"../core/fields\";\nimport {\n useActivities,\n type Activity,\n type ActivitySlug,\n} from \"../hooks/use-activities\";\nimport { ErrorState } from \"../components/error-state\";\nimport {\n AudioWaveform,\n Bell,\n Calendar,\n CheckSquare,\n Eye,\n MessageCircle,\n Play,\n ShoppingCart,\n Star,\n Trophy,\n User,\n UserPlus,\n type LucideIcon,\n} from \"lucide-react\";\n\n// ---------- helpers ----------\n\nconst RELATIVE_TIME_REFRESH_MS = 60_000;\nconst DEFAULT_MAX_ITEMS_TO_SHOW = 6;\nconst MAX_ITEMS_TO_SHOW_LIMIT = 20;\n\nconst ACTIVITY_ICON_MAP: Record<ActivitySlug, LucideIcon> = {\n order_placed: ShoppingCart,\n abandoned_cart: ShoppingCart,\n cart_items_added: ShoppingCart,\n new_cart_items_added: ShoppingCart,\n direct_message: MessageCircle,\n comment_reply: MessageCircle,\n message_received: MessageCircle,\n message_sent: MessageCircle,\n video: Play,\n video_complete: Play,\n video_contact: Play,\n video_complete_contact: Play,\n new_lead: UserPlus,\n page_views_contact: UserPlus,\n smart_link_clicked: UserPlus,\n page_views: Eye,\n upcoming_event: Calendar,\n review_left: Star,\n tasks: CheckSquare,\n announcements: Bell,\n fantasy_point: Trophy,\n};\n\nconst getActivityIcon = (slug: ActivitySlug): LucideIcon =>\n ACTIVITY_ICON_MAP[slug] ?? User;\n\nconst formatAbsoluteTime = (timestamp: string): string =>\n new Date(timestamp).toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true,\n });\n\nconst formatRelativeTime = (timestamp: string, now: number): string => {\n const then = new Date(timestamp).getTime();\n const diffSec = Math.max(0, Math.round((now - then) / 1000));\n\n if (diffSec < 60) return \"now\";\n const diffMin = Math.floor(diffSec / 60);\n if (diffMin < 60) return `${diffMin}m`;\n const diffHr = Math.floor(diffMin / 60);\n if (diffHr < 24) return `${diffHr}h`;\n const diffDay = Math.floor(diffHr / 24);\n if (diffDay < 7) return `${diffDay}d`;\n return new Date(timestamp).toLocaleDateString(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n });\n};\n\nconst toLocalDateKey = (date: Date): string => {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n return `${year}-${month}-${day}`;\n};\n\nconst formatDateHeader = (timestamp: string, nowMs: number): string => {\n const date = new Date(timestamp);\n if (!Number.isFinite(date.getTime())) return timestamp;\n\n const now = new Date(nowMs);\n const yesterday = new Date(now);\n yesterday.setDate(now.getDate() - 1);\n\n const todayKey = toLocalDateKey(now);\n const yesterdayKey = toLocalDateKey(yesterday);\n const dateKey = toLocalDateKey(date);\n\n if (dateKey === todayKey) return \"Today\";\n if (dateKey === yesterdayKey) return \"Yesterday\";\n return date.toLocaleDateString(\"en-US\", {\n month: \"long\",\n day: \"numeric\",\n });\n};\n\nconst getDateKey = (timestamp: string): string => {\n const date = new Date(timestamp);\n if (!Number.isFinite(date.getTime())) return timestamp;\n return toLocalDateKey(date);\n};\n\nconst getColorCssValue = (color: ColorOptions): string =>\n color === \"transparent\" ? \"transparent\" : `var(--color-${color})`;\n\nconst normalizeMaxItemsToShow = (value: number): number => {\n if (!Number.isFinite(value)) return DEFAULT_MAX_ITEMS_TO_SHOW;\n return Math.min(MAX_ITEMS_TO_SHOW_LIMIT, Math.max(1, Math.floor(value)));\n};\n\nconst shouldRenderEyebrow = (\n showEyebrow: boolean,\n titleText: string,\n): boolean =>\n showEyebrow && titleText.trim().toLowerCase() !== \"recent activity\";\n\nconst isUnknownUser = (name: string | undefined): boolean => {\n if (!name) return true;\n const lower = name.trim().toLowerCase();\n if (!lower) return true;\n return (\n lower === \"unknown\" ||\n lower.includes(\"unknown visitor\") ||\n lower === \"anonymous\" ||\n lower === \"guest\"\n );\n};\n\nconst getInitials = (name: string | undefined): string => {\n if (!name) return \"\";\n const parts = name.trim().split(/\\s+/).slice(0, 2);\n return parts.map((p) => p[0]?.toUpperCase() ?? \"\").join(\"\");\n};\n\n// ---------- avatar ----------\n\ntype AvatarProps = {\n activity: Activity;\n textColor: ColorOptions;\n accentColor: ColorOptions;\n surfaceColor: ColorOptions;\n showTypeBadge: boolean;\n};\n\nfunction Avatar({\n activity,\n textColor,\n accentColor,\n surfaceColor,\n showTypeBadge,\n}: AvatarProps) {\n const TypeIcon = getActivityIcon(activity.slug);\n const unknown = isUnknownUser(activity.userName);\n const initials = unknown ? \"\" : getInitials(activity.userName);\n // 3-tier fallback: image → initials → person icon. If avatarUrl 404s or\n // fails to decode, flip to the next tier instead of showing a broken image.\n const [failedAvatarUrl, setFailedAvatarUrl] = useState<string | null>(null);\n const avatarUrl = activity.avatarUrl;\n const showImage = Boolean(avatarUrl) && failedAvatarUrl !== avatarUrl;\n\n return (\n <div className=\"relative shrink-0\">\n {showImage ? (\n <img\n src={avatarUrl ?? undefined}\n alt={unknown ? \"\" : activity.userName}\n width={36}\n height={36}\n loading=\"lazy\"\n onError={() => setFailedAvatarUrl(avatarUrl)}\n className=\"size-9 rounded-full object-cover\"\n style={{\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n />\n ) : initials ? (\n <div\n className={`flex size-9 items-center justify-center rounded-full text-[11px] font-bold tracking-[0.04em] text-${textColor}/80`}\n style={{\n background: `color-mix(in oklch, var(--color-${accentColor}) 12%, transparent)`,\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n aria-label={activity.userName}\n >\n {initials}\n </div>\n ) : (\n <div\n className={`flex size-9 items-center justify-center rounded-full`}\n style={{\n background: `color-mix(in oklch, var(--color-${textColor}) 5%, transparent)`,\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n aria-label=\"Unknown visitor\"\n >\n <User className={`size-3.5 text-${textColor}/55`} />\n </div>\n )}\n\n {/* Type badge — shows what kind of event, regardless of avatar kind. */}\n {showTypeBadge && (\n <span\n className={`bg-${accentColor} text-${accentColor}-foreground absolute -right-0.5 -bottom-0.5 flex size-4 items-center justify-center rounded-full`}\n style={{\n boxShadow: `0 0 0 2px ${getColorCssValue(surfaceColor)}`,\n }}\n aria-hidden=\"true\"\n >\n <TypeIcon style={{ width: 8, height: 8 }} />\n </span>\n )}\n </div>\n );\n}\n\n// ---------- activity row ----------\n\ntype ActivityRowProps = {\n activity: Activity;\n accentColor: ColorOptions;\n textColor: ColorOptions;\n surfaceColor: ColorOptions;\n showRelativeTime: boolean;\n nowMs: number;\n};\n\nfunction ActivityRow({\n activity,\n accentColor,\n textColor,\n surfaceColor,\n showRelativeTime,\n nowMs,\n}: ActivityRowProps) {\n const timeText = showRelativeTime\n ? formatRelativeTime(activity.timestamp, nowMs)\n : formatAbsoluteTime(activity.timestamp);\n\n const absoluteTooltip = new Date(activity.timestamp).toLocaleString(\"en-US\", {\n weekday: \"short\",\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n\n return (\n <li\n role=\"article\"\n className=\"relative flex items-start gap-3 rounded-md px-2 py-2\"\n >\n <Avatar\n activity={activity}\n textColor={textColor}\n accentColor={accentColor}\n surfaceColor={surfaceColor}\n showTypeBadge\n />\n\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <div className=\"flex items-baseline gap-2\">\n <p\n className={`min-w-0 flex-1 truncate text-[13px] leading-tight font-semibold text-${textColor}`}\n >\n {activity.activityType}\n </p>\n <span\n title={absoluteTooltip}\n className={`shrink-0 font-mono text-[10px] font-medium tabular-nums text-${textColor}/45`}\n >\n {timeText}\n </span>\n </div>\n <p\n className={`mt-0.5 truncate text-[12px] leading-snug text-${textColor}/65`}\n >\n <span className={`font-semibold text-${textColor}/85`}>\n {activity.userName}\n </span>\n {activity.targetName && (\n <>\n <span className={`mx-1 text-${textColor}/35`}>·</span>\n <span>{activity.targetName}</span>\n </>\n )}\n </p>\n </div>\n </li>\n );\n}\n\n// ---------- widget ----------\n\ntype RecentActivityWidgetProps = ComponentProps<\"div\"> & {\n titleEnabled?: boolean;\n titleText?: string;\n titleFontSize?: FontSizeOptions;\n titleColor?: ColorOptions;\n\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n borderWidth?: BorderWidthOptions;\n borderColor?: ColorOptions;\n\n maxItemsToShow?: number;\n\n // Polish (additive)\n showTimeline?: boolean;\n showRelativeTime?: boolean;\n showCountChip?: boolean;\n showEyebrow?: boolean;\n};\n\nfunction useRelativeTimeNow(): number {\n const [nowMs, setNowMs] = useState(() => Date.now());\n\n useEffect(() => {\n const timer = window.setInterval(() => {\n setNowMs(Date.now());\n }, RELATIVE_TIME_REFRESH_MS);\n\n return () => window.clearInterval(timer);\n }, []);\n\n return nowMs;\n}\n\nexport function RecentActivityWidget({\n titleEnabled = true,\n titleText = \"Activity\",\n titleFontSize = \"xl\",\n titleColor = \"foreground\",\n\n background = { type: \"solid\", color: \"background\" },\n textColor = \"foreground\",\n accentColor = \"primary\",\n padding = 4,\n borderRadius = \"md\",\n borderWidth = \"thin\",\n borderColor = \"muted\",\n\n maxItemsToShow = 6,\n\n showTimeline = true,\n showRelativeTime = true,\n showCountChip = true,\n showEyebrow = true,\n\n className,\n ...props\n}: RecentActivityWidgetProps): React.JSX.Element {\n const backgroundColor = background.color || \"background\";\n const backgroundImage =\n (background.resource?.image_url || background.resource?.imageUrl) &&\n background.type === \"image\"\n ? `url(${background.resource.image_url || background.resource.imageUrl})`\n : \"none\";\n\n const { data: activities = [], isLoading, isError } = useActivities();\n const nowMs = useRelativeTimeNow();\n const maxVisibleItems = normalizeMaxItemsToShow(maxItemsToShow);\n\n const activitiesToShow = useMemo(() => {\n if (activities.length === 0)\n return [] as { date: string; items: Activity[] }[];\n const groups = new Map<string, Activity[]>();\n for (const activity of activities.slice(0, maxVisibleItems)) {\n const key = getDateKey(activity.timestamp);\n const existing = groups.get(key);\n if (existing) existing.push(activity);\n else groups.set(key, [activity]);\n }\n return Array.from(groups.entries()).map(([key, items]) => ({\n // `key` is only a defensive fallback; every group is created with at\n // least one activity, so the item timestamp is what normally labels it.\n date: formatDateHeader(items[0]?.timestamp ?? key, nowMs),\n items,\n }));\n }, [activities, maxVisibleItems, nowMs]);\n\n const totalCount = Math.min(activities.length, maxVisibleItems);\n\n return (\n <div\n className={`@container overflow-hidden rounded-${borderRadius} bg-${backgroundColor} text-${textColor} ${borderWidthClasses[borderWidth]} ${borderWidth !== \"none\" ? borderColorClasses[borderColor] : \"\"} ${className ?? \"\"}`}\n style={{\n backgroundImage,\n // Soft ambient lift derived from theme tokens. The configured border\n // props own the crisp outline so saved border settings stay intact.\n boxShadow: `0 1px 2px color-mix(in oklch, var(--color-foreground) 4%, transparent), 0 14px 32px -20px color-mix(in oklch, var(--color-foreground) 14%, transparent)`,\n }}\n {...props}\n >\n <div className={`p-${padding} flex flex-col`}>\n {/* Header */}\n {titleEnabled && titleText && (\n <div className=\"mb-4 flex items-end justify-between gap-3\">\n <div className=\"flex min-w-0 flex-col\">\n {shouldRenderEyebrow(showEyebrow, titleText) && (\n <span\n className={`text-[10px] font-bold tracking-[0.2em] uppercase text-${textColor}/55`}\n >\n Recent\n </span>\n )}\n <h2\n className={`mt-0.5 text-${titleFontSize} leading-tight font-bold tracking-[-0.015em] text-${titleColor}`}\n >\n {titleText}\n </h2>\n </div>\n {!isLoading && !isError && showCountChip && totalCount > 0 && (\n <span\n className={`inline-flex shrink-0 items-center gap-1.5 rounded-full bg-${accentColor}/10 px-2 py-1 text-[10px] font-bold tracking-[0.06em] text-${accentColor}`}\n >\n <span\n aria-hidden=\"true\"\n className={`size-1.5 rounded-full bg-${accentColor}`}\n />\n {totalCount} {totalCount === 1 ? \"update\" : \"updates\"}\n </span>\n )}\n </div>\n )}\n\n {/* States */}\n {isLoading ? (\n <ActivitySkeleton\n textColor={textColor}\n showTimeline={showTimeline}\n rowCount={maxVisibleItems}\n />\n ) : isError ? (\n <ErrorState />\n ) : activities.length === 0 ? (\n <EmptyState textColor={textColor} accentColor={accentColor} />\n ) : (\n /* Feed */\n <div role=\"feed\" className=\"flex flex-col gap-3\">\n {activitiesToShow.map((group, groupIdx) => (\n <section\n key={`${group.date}-${groupIdx}`}\n className=\"flex flex-col gap-1\"\n >\n <header className=\"flex items-center gap-2 px-2\">\n <span\n className={`text-[10px] font-bold tracking-[0.18em] uppercase text-${textColor}/55`}\n >\n {group.date}\n </span>\n <span\n aria-hidden=\"true\"\n className=\"h-px flex-1\"\n style={{\n background: `color-mix(in oklch, var(--color-${textColor}) 8%, transparent)`,\n }}\n />\n </header>\n\n <ul className=\"relative flex flex-col\">\n {/* Single continuous spine — anchored to the first and last\n avatar centers. Avatar is size-9 (36px), inside a li with\n px-2 py-2 (8px padding). So avatar center = 8 + 18 = 26px\n from the edges, which is also where the spine lives. */}\n {showTimeline && group.items.length > 1 && (\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute top-[26px] bottom-[26px] left-[26px] w-px\"\n style={{\n background: `color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n />\n )}\n {group.items.map((activity) => (\n <ActivityRow\n key={activity.id}\n activity={activity}\n accentColor={accentColor}\n textColor={textColor}\n surfaceColor={backgroundColor}\n showRelativeTime={showRelativeTime}\n nowMs={nowMs}\n />\n ))}\n </ul>\n </section>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n\n// ---------- empty state ----------\n\ntype EmptyStateProps = {\n textColor: ColorOptions;\n accentColor: ColorOptions;\n};\n\nfunction EmptyState({ textColor, accentColor }: EmptyStateProps) {\n return (\n <div className=\"flex flex-col items-center justify-center gap-3 px-4 py-10\">\n <div\n className=\"relative flex size-12 items-center justify-center rounded-full\"\n style={{\n background: `color-mix(in oklch, var(--color-${accentColor}) 10%, transparent)`,\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${textColor}) 8%, transparent)`,\n }}\n >\n <AudioWaveform className={`size-5 text-${accentColor}/80`} />\n </div>\n <div className=\"flex flex-col items-center gap-1\">\n <p className={`text-[13px] font-semibold text-${textColor}/80`}>\n No activity yet\n </p>\n <p\n className={`max-w-[24ch] text-center text-[11px] leading-snug text-${textColor}/55`}\n >\n Orders, messages, views and leads will show up here as they happen.\n </p>\n </div>\n </div>\n );\n}\n\n// ---------- skeleton ----------\n\ntype ActivitySkeletonProps = {\n textColor: ColorOptions;\n showTimeline: boolean;\n rowCount: number;\n};\n\nfunction ActivitySkeleton({\n textColor,\n showTimeline,\n rowCount,\n}: ActivitySkeletonProps) {\n return (\n <div className=\"relative flex flex-col\" aria-busy=\"true\">\n {showTimeline && (\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute top-[26px] bottom-[26px] left-[26px] w-px\"\n style={{\n background: `color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n />\n )}\n {Array.from({ length: rowCount }, (_, i) => i).map((i) => (\n <div\n key={i}\n className=\"relative flex items-start gap-3 rounded-md px-2 py-2\"\n >\n <div\n className={`size-9 shrink-0 animate-pulse rounded-full bg-${textColor}/10`}\n />\n <div className=\"flex min-w-0 flex-1 flex-col gap-1.5\">\n <div\n className={`h-2 w-2/3 animate-pulse rounded-full bg-${textColor}/10`}\n />\n <div\n className={`h-2 w-1/2 animate-pulse rounded-full bg-${textColor}/10`}\n />\n </div>\n </div>\n ))}\n </div>\n );\n}\n\n// ---------- schema ----------\n\nexport const recentActivityWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"RecentActivityWidget\",\n displayName: \"Recent Activity Widget\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [\n // Title\n {\n key: \"titleEnabled\",\n label: \"Widget Title\",\n type: \"boolean\",\n description: \"Display the title and eyebrow above the feed\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n },\n {\n key: \"titleText\",\n label: \"Title\",\n type: \"text\",\n description: \"Title text displayed above the activity feed\",\n defaultValue: \"Activity\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n getFontSizeField({\n key: \"titleFontSize\",\n label: \"Title Font Size\",\n description: \"Font size for the widget title\",\n defaultValue: \"xl\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n getColorField({\n key: \"titleColor\",\n label: \"Title Color\",\n description: \"Color for the widget title\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n {\n key: \"showEyebrow\",\n label: \"Show 'RECENT' Eyebrow\",\n type: \"boolean\",\n description: \"Small uppercase label above the title\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n {\n key: \"showCountChip\",\n label: \"Show Updates Count Chip\",\n type: \"boolean\",\n description: \"Accent pill next to the title showing how many updates\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n\n // Feed\n {\n key: \"maxItemsToShow\",\n label: \"Max Items\",\n type: \"number\",\n description: \"Maximum number of activity items to display\",\n defaultValue: DEFAULT_MAX_ITEMS_TO_SHOW,\n min: 1,\n max: MAX_ITEMS_TO_SHOW_LIMIT,\n step: 1,\n tab: \"styling\",\n group: \"Feed\",\n },\n {\n key: \"showTimeline\",\n label: \"Timeline Spine\",\n type: \"boolean\",\n description: \"Draw a vertical line connecting activity avatars\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Feed\",\n },\n {\n key: \"showRelativeTime\",\n label: \"Relative Timestamps\",\n type: \"boolean\",\n description: \"Show 'now / 2m / 1h / 3d' instead of absolute times\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Feed\",\n },\n\n // Design\n {\n type: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Background for the widget container\",\n defaultValue: \"background\",\n tab: \"styling\",\n group: \"Design\",\n },\n getColorField({\n key: \"textColor\",\n label: \"Text Color\",\n description: \"Default text color for activity content\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getColorField({\n key: \"accentColor\",\n label: \"Accent Color\",\n description:\n \"Drives the count chip, type badge, initials avatar, and empty-state icon\",\n defaultValue: \"primary\",\n tab: \"styling\",\n group: \"Design\",\n }),\n {\n key: \"separator\",\n type: \"separator\",\n label: \"Separator\",\n tab: \"styling\",\n group: \"Design\",\n },\n getPaddingField({\n key: \"padding\",\n label: \"Padding\",\n description: \"Padding around the widget container\",\n defaultValue: 4,\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderRadiusField({\n key: \"borderRadius\",\n label: \"Border Radius\",\n description: \"Border radius for the widget container\",\n defaultValue: \"md\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderWidthField({\n key: \"borderWidth\",\n label: \"Border Width\",\n description: \"Border width for the widget container\",\n defaultValue: \"thin\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderColorField({\n key: \"borderColor\",\n label: \"Border Color\",\n description: \"Border color for the widget container\",\n defaultValue: \"muted\",\n tab: \"styling\",\n group: \"Design\",\n }),\n ],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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C;EACF;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EAGD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,KAAK;GACL,MAAM;GACN,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EAGD;GACE,MAAM;GACN,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aACE;GACF,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF;GACE,KAAK;GACL,MAAM;GACN,OAAO;GACP,KAAK;GACL,OAAO;GACR;EACD,gBAAgB;GACd,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,qBAAqB;GACnB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,oBAAoB;GAClB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,oBAAoB;GAClB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACH;CACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-9hOWP6kD.cjs");
|
|
2
|
-
const require_registry_context = require("./registry-context-
|
|
2
|
+
const require_registry_context = require("./registry-context-CcZYS15Q.cjs");
|
|
3
3
|
const require_error_state = require("./error-state-BDhSltIa.cjs");
|
|
4
4
|
const require_registries = require("./registries-DBb6VjAX.cjs");
|
|
5
5
|
let react = require("react");
|
|
@@ -578,4 +578,4 @@ Object.defineProperty(exports, "recentActivityWidgetPropertySchema", {
|
|
|
578
578
|
}
|
|
579
579
|
});
|
|
580
580
|
|
|
581
|
-
//# sourceMappingURL=RecentActivityWidget-
|
|
581
|
+
//# sourceMappingURL=RecentActivityWidget-DNyhUZNs.cjs.map
|
package/dist/{RecentActivityWidget-BNW9aFT4.cjs.map → RecentActivityWidget-DNyhUZNs.cjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RecentActivityWidget-BNW9aFT4.cjs","names":["useWidgetsApi","useWidgetPreviewContext","useDataSourceRegistryConfig","ShoppingCart","MessageCircle","Play","UserPlus","Eye","Calendar","Star","CheckSquare","Bell","Trophy","User","borderWidthClasses","borderColorClasses","ErrorState","AudioWaveform","getFontSizeField","getColorField","getPaddingField","getBorderRadiusField","getBorderWidthField","getBorderColorField"],"sources":["../../widgets/src/hooks/use-activities.preview.ts","../../widgets/src/hooks/use-activities.ts","../../widgets/src/widgets/RecentActivityWidget.tsx"],"sourcesContent":["import type { Activity } from \"@fluid-app/portal-core/widgets-api-types\";\n\nconst now = new Date();\n\nfunction minutesAgo(minutes: number): string {\n return new Date(now.getTime() - minutes * 60_000).toISOString();\n}\n\nexport const PREVIEW_DATA: Activity[] = [\n {\n id: 1,\n userName: \"Sarah Johnson\",\n avatarUrl: null,\n activityType: \"Order Placed\",\n targetName: \"Wellness Starter Kit\",\n timestamp: minutesAgo(12),\n slug: \"order_placed\",\n },\n {\n id: 2,\n userName: \"Mike Chen\",\n avatarUrl: null,\n activityType: \"New Lead\",\n targetName: \"Signed up from Instagram link\",\n timestamp: minutesAgo(45),\n slug: \"new_lead\",\n },\n {\n id: 3,\n userName: \"Visitor from Austin, TX\",\n avatarUrl: null,\n activityType: \"Page Views\",\n targetName: \"Viewed product catalog (3 pages)\",\n timestamp: minutesAgo(90),\n slug: \"page_views\",\n },\n {\n id: 4,\n userName: \"Lisa Park\",\n avatarUrl: null,\n activityType: \"Video Complete\",\n targetName: \"Watched: Getting Started Guide\",\n timestamp: minutesAgo(180),\n slug: \"video_complete\",\n },\n];\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useWidgetsApi } from \"@fluid-app/portal-core/widgets-api-context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useDataSourceRegistryConfig } from \"@fluid-app/portal-react/data-sources/registry-context\";\nimport { PREVIEW_DATA } from \"./use-activities.preview\";\nimport type { Activity } from \"@fluid-app/portal-core/widgets-api-types\";\n\nexport type {\n Activity,\n ActivitySlug,\n} from \"@fluid-app/portal-core/widgets-api-types\";\n\nexport function useActivities(): UseQueryResult<Activity[], Error> {\n const widgetsApi = useWidgetsApi();\n const { isPreview } = useWidgetPreviewContext();\n const { baseUrl } = useDataSourceRegistryConfig();\n\n return useQuery({\n queryKey: [\n \"portal-widget-use\",\n \"activities\",\n isPreview ? \"preview\" : baseUrl,\n ] as const,\n queryFn: ({ signal }) => widgetsApi.fetchActivities(signal),\n enabled: !isPreview,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\n });\n}\n","import { useEffect, useMemo, useState, type ComponentProps } from \"react\";\nimport type React from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n BorderWidthOptions,\n ColorOptions,\n FontSizeOptions,\n PaddingOptions,\n} from \"@fluid-app/portal-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport {\n borderColorClasses,\n borderWidthClasses,\n getBorderColorField,\n getBorderRadiusField,\n getBorderWidthField,\n getColorField,\n getFontSizeField,\n getPaddingField,\n} from \"../core/fields\";\nimport {\n useActivities,\n type Activity,\n type ActivitySlug,\n} from \"../hooks/use-activities\";\nimport { ErrorState } from \"../components/error-state\";\nimport {\n AudioWaveform,\n Bell,\n Calendar,\n CheckSquare,\n Eye,\n MessageCircle,\n Play,\n ShoppingCart,\n Star,\n Trophy,\n User,\n UserPlus,\n type LucideIcon,\n} from \"lucide-react\";\n\n// ---------- helpers ----------\n\nconst RELATIVE_TIME_REFRESH_MS = 60_000;\nconst DEFAULT_MAX_ITEMS_TO_SHOW = 6;\nconst MAX_ITEMS_TO_SHOW_LIMIT = 20;\n\nconst ACTIVITY_ICON_MAP: Record<ActivitySlug, LucideIcon> = {\n order_placed: ShoppingCart,\n abandoned_cart: ShoppingCart,\n cart_items_added: ShoppingCart,\n new_cart_items_added: ShoppingCart,\n direct_message: MessageCircle,\n comment_reply: MessageCircle,\n message_received: MessageCircle,\n message_sent: MessageCircle,\n video: Play,\n video_complete: Play,\n video_contact: Play,\n video_complete_contact: Play,\n new_lead: UserPlus,\n page_views_contact: UserPlus,\n smart_link_clicked: UserPlus,\n page_views: Eye,\n upcoming_event: Calendar,\n review_left: Star,\n tasks: CheckSquare,\n announcements: Bell,\n fantasy_point: Trophy,\n};\n\nconst getActivityIcon = (slug: ActivitySlug): LucideIcon =>\n ACTIVITY_ICON_MAP[slug] ?? User;\n\nconst formatAbsoluteTime = (timestamp: string): string =>\n new Date(timestamp).toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true,\n });\n\nconst formatRelativeTime = (timestamp: string, now: number): string => {\n const then = new Date(timestamp).getTime();\n const diffSec = Math.max(0, Math.round((now - then) / 1000));\n\n if (diffSec < 60) return \"now\";\n const diffMin = Math.floor(diffSec / 60);\n if (diffMin < 60) return `${diffMin}m`;\n const diffHr = Math.floor(diffMin / 60);\n if (diffHr < 24) return `${diffHr}h`;\n const diffDay = Math.floor(diffHr / 24);\n if (diffDay < 7) return `${diffDay}d`;\n return new Date(timestamp).toLocaleDateString(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n });\n};\n\nconst toLocalDateKey = (date: Date): string => {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n return `${year}-${month}-${day}`;\n};\n\nconst formatDateHeader = (timestamp: string, nowMs: number): string => {\n const date = new Date(timestamp);\n if (!Number.isFinite(date.getTime())) return timestamp;\n\n const now = new Date(nowMs);\n const yesterday = new Date(now);\n yesterday.setDate(now.getDate() - 1);\n\n const todayKey = toLocalDateKey(now);\n const yesterdayKey = toLocalDateKey(yesterday);\n const dateKey = toLocalDateKey(date);\n\n if (dateKey === todayKey) return \"Today\";\n if (dateKey === yesterdayKey) return \"Yesterday\";\n return date.toLocaleDateString(\"en-US\", {\n month: \"long\",\n day: \"numeric\",\n });\n};\n\nconst getDateKey = (timestamp: string): string => {\n const date = new Date(timestamp);\n if (!Number.isFinite(date.getTime())) return timestamp;\n return toLocalDateKey(date);\n};\n\nconst getColorCssValue = (color: ColorOptions): string =>\n color === \"transparent\" ? \"transparent\" : `var(--color-${color})`;\n\nconst normalizeMaxItemsToShow = (value: number): number => {\n if (!Number.isFinite(value)) return DEFAULT_MAX_ITEMS_TO_SHOW;\n return Math.min(MAX_ITEMS_TO_SHOW_LIMIT, Math.max(1, Math.floor(value)));\n};\n\nconst shouldRenderEyebrow = (\n showEyebrow: boolean,\n titleText: string,\n): boolean =>\n showEyebrow && titleText.trim().toLowerCase() !== \"recent activity\";\n\nconst isUnknownUser = (name: string | undefined): boolean => {\n if (!name) return true;\n const lower = name.trim().toLowerCase();\n if (!lower) return true;\n return (\n lower === \"unknown\" ||\n lower.includes(\"unknown visitor\") ||\n lower === \"anonymous\" ||\n lower === \"guest\"\n );\n};\n\nconst getInitials = (name: string | undefined): string => {\n if (!name) return \"\";\n const parts = name.trim().split(/\\s+/).slice(0, 2);\n return parts.map((p) => p[0]?.toUpperCase() ?? \"\").join(\"\");\n};\n\n// ---------- avatar ----------\n\ntype AvatarProps = {\n activity: Activity;\n textColor: ColorOptions;\n accentColor: ColorOptions;\n surfaceColor: ColorOptions;\n showTypeBadge: boolean;\n};\n\nfunction Avatar({\n activity,\n textColor,\n accentColor,\n surfaceColor,\n showTypeBadge,\n}: AvatarProps) {\n const TypeIcon = getActivityIcon(activity.slug);\n const unknown = isUnknownUser(activity.userName);\n const initials = unknown ? \"\" : getInitials(activity.userName);\n // 3-tier fallback: image → initials → person icon. If avatarUrl 404s or\n // fails to decode, flip to the next tier instead of showing a broken image.\n const [failedAvatarUrl, setFailedAvatarUrl] = useState<string | null>(null);\n const avatarUrl = activity.avatarUrl;\n const showImage = Boolean(avatarUrl) && failedAvatarUrl !== avatarUrl;\n\n return (\n <div className=\"relative shrink-0\">\n {showImage ? (\n <img\n src={avatarUrl ?? undefined}\n alt={unknown ? \"\" : activity.userName}\n width={36}\n height={36}\n loading=\"lazy\"\n onError={() => setFailedAvatarUrl(avatarUrl)}\n className=\"size-9 rounded-full object-cover\"\n style={{\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n />\n ) : initials ? (\n <div\n className={`flex size-9 items-center justify-center rounded-full text-[11px] font-bold tracking-[0.04em] text-${textColor}/80`}\n style={{\n background: `color-mix(in oklch, var(--color-${accentColor}) 12%, transparent)`,\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n aria-label={activity.userName}\n >\n {initials}\n </div>\n ) : (\n <div\n className={`flex size-9 items-center justify-center rounded-full`}\n style={{\n background: `color-mix(in oklch, var(--color-${textColor}) 5%, transparent)`,\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n aria-label=\"Unknown visitor\"\n >\n <User className={`size-3.5 text-${textColor}/55`} />\n </div>\n )}\n\n {/* Type badge — shows what kind of event, regardless of avatar kind. */}\n {showTypeBadge && (\n <span\n className={`bg-${accentColor} text-${accentColor}-foreground absolute -right-0.5 -bottom-0.5 flex size-4 items-center justify-center rounded-full`}\n style={{\n boxShadow: `0 0 0 2px ${getColorCssValue(surfaceColor)}`,\n }}\n aria-hidden=\"true\"\n >\n <TypeIcon style={{ width: 8, height: 8 }} />\n </span>\n )}\n </div>\n );\n}\n\n// ---------- activity row ----------\n\ntype ActivityRowProps = {\n activity: Activity;\n accentColor: ColorOptions;\n textColor: ColorOptions;\n surfaceColor: ColorOptions;\n showRelativeTime: boolean;\n nowMs: number;\n};\n\nfunction ActivityRow({\n activity,\n accentColor,\n textColor,\n surfaceColor,\n showRelativeTime,\n nowMs,\n}: ActivityRowProps) {\n const timeText = showRelativeTime\n ? formatRelativeTime(activity.timestamp, nowMs)\n : formatAbsoluteTime(activity.timestamp);\n\n const absoluteTooltip = new Date(activity.timestamp).toLocaleString(\"en-US\", {\n weekday: \"short\",\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n\n return (\n <li\n role=\"article\"\n className=\"relative flex items-start gap-3 rounded-md px-2 py-2\"\n >\n <Avatar\n activity={activity}\n textColor={textColor}\n accentColor={accentColor}\n surfaceColor={surfaceColor}\n showTypeBadge\n />\n\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <div className=\"flex items-baseline gap-2\">\n <p\n className={`min-w-0 flex-1 truncate text-[13px] leading-tight font-semibold text-${textColor}`}\n >\n {activity.activityType}\n </p>\n <span\n title={absoluteTooltip}\n className={`shrink-0 font-mono text-[10px] font-medium tabular-nums text-${textColor}/45`}\n >\n {timeText}\n </span>\n </div>\n <p\n className={`mt-0.5 truncate text-[12px] leading-snug text-${textColor}/65`}\n >\n <span className={`font-semibold text-${textColor}/85`}>\n {activity.userName}\n </span>\n {activity.targetName && (\n <>\n <span className={`mx-1 text-${textColor}/35`}>·</span>\n <span>{activity.targetName}</span>\n </>\n )}\n </p>\n </div>\n </li>\n );\n}\n\n// ---------- widget ----------\n\ntype RecentActivityWidgetProps = ComponentProps<\"div\"> & {\n titleEnabled?: boolean;\n titleText?: string;\n titleFontSize?: FontSizeOptions;\n titleColor?: ColorOptions;\n\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n borderWidth?: BorderWidthOptions;\n borderColor?: ColorOptions;\n\n maxItemsToShow?: number;\n\n // Polish (additive)\n showTimeline?: boolean;\n showRelativeTime?: boolean;\n showCountChip?: boolean;\n showEyebrow?: boolean;\n};\n\nfunction useRelativeTimeNow(): number {\n const [nowMs, setNowMs] = useState(() => Date.now());\n\n useEffect(() => {\n const timer = window.setInterval(() => {\n setNowMs(Date.now());\n }, RELATIVE_TIME_REFRESH_MS);\n\n return () => window.clearInterval(timer);\n }, []);\n\n return nowMs;\n}\n\nexport function RecentActivityWidget({\n titleEnabled = true,\n titleText = \"Activity\",\n titleFontSize = \"xl\",\n titleColor = \"foreground\",\n\n background = { type: \"solid\", color: \"background\" },\n textColor = \"foreground\",\n accentColor = \"primary\",\n padding = 4,\n borderRadius = \"md\",\n borderWidth = \"thin\",\n borderColor = \"muted\",\n\n maxItemsToShow = 6,\n\n showTimeline = true,\n showRelativeTime = true,\n showCountChip = true,\n showEyebrow = true,\n\n className,\n ...props\n}: RecentActivityWidgetProps): React.JSX.Element {\n const backgroundColor = background.color || \"background\";\n const backgroundImage =\n (background.resource?.image_url || background.resource?.imageUrl) &&\n background.type === \"image\"\n ? `url(${background.resource.image_url || background.resource.imageUrl})`\n : \"none\";\n\n const { data: activities = [], isLoading, isError } = useActivities();\n const nowMs = useRelativeTimeNow();\n const maxVisibleItems = normalizeMaxItemsToShow(maxItemsToShow);\n\n const activitiesToShow = useMemo(() => {\n if (activities.length === 0)\n return [] as { date: string; items: Activity[] }[];\n const groups = new Map<string, Activity[]>();\n for (const activity of activities.slice(0, maxVisibleItems)) {\n const key = getDateKey(activity.timestamp);\n const existing = groups.get(key);\n if (existing) existing.push(activity);\n else groups.set(key, [activity]);\n }\n return Array.from(groups.entries()).map(([key, items]) => ({\n // `key` is only a defensive fallback; every group is created with at\n // least one activity, so the item timestamp is what normally labels it.\n date: formatDateHeader(items[0]?.timestamp ?? key, nowMs),\n items,\n }));\n }, [activities, maxVisibleItems, nowMs]);\n\n const totalCount = Math.min(activities.length, maxVisibleItems);\n\n return (\n <div\n className={`@container overflow-hidden rounded-${borderRadius} bg-${backgroundColor} text-${textColor} ${borderWidthClasses[borderWidth]} ${borderWidth !== \"none\" ? borderColorClasses[borderColor] : \"\"} ${className ?? \"\"}`}\n style={{\n backgroundImage,\n // Soft ambient lift derived from theme tokens. The configured border\n // props own the crisp outline so saved border settings stay intact.\n boxShadow: `0 1px 2px color-mix(in oklch, var(--color-foreground) 4%, transparent), 0 14px 32px -20px color-mix(in oklch, var(--color-foreground) 14%, transparent)`,\n }}\n {...props}\n >\n <div className={`p-${padding} flex flex-col`}>\n {/* Header */}\n {titleEnabled && titleText && (\n <div className=\"mb-4 flex items-end justify-between gap-3\">\n <div className=\"flex min-w-0 flex-col\">\n {shouldRenderEyebrow(showEyebrow, titleText) && (\n <span\n className={`text-[10px] font-bold tracking-[0.2em] uppercase text-${textColor}/55`}\n >\n Recent\n </span>\n )}\n <h2\n className={`mt-0.5 text-${titleFontSize} leading-tight font-bold tracking-[-0.015em] text-${titleColor}`}\n >\n {titleText}\n </h2>\n </div>\n {!isLoading && !isError && showCountChip && totalCount > 0 && (\n <span\n className={`inline-flex shrink-0 items-center gap-1.5 rounded-full bg-${accentColor}/10 px-2 py-1 text-[10px] font-bold tracking-[0.06em] text-${accentColor}`}\n >\n <span\n aria-hidden=\"true\"\n className={`size-1.5 rounded-full bg-${accentColor}`}\n />\n {totalCount} {totalCount === 1 ? \"update\" : \"updates\"}\n </span>\n )}\n </div>\n )}\n\n {/* States */}\n {isLoading ? (\n <ActivitySkeleton\n textColor={textColor}\n showTimeline={showTimeline}\n rowCount={maxVisibleItems}\n />\n ) : isError ? (\n <ErrorState />\n ) : activities.length === 0 ? (\n <EmptyState textColor={textColor} accentColor={accentColor} />\n ) : (\n /* Feed */\n <div role=\"feed\" className=\"flex flex-col gap-3\">\n {activitiesToShow.map((group, groupIdx) => (\n <section\n key={`${group.date}-${groupIdx}`}\n className=\"flex flex-col gap-1\"\n >\n <header className=\"flex items-center gap-2 px-2\">\n <span\n className={`text-[10px] font-bold tracking-[0.18em] uppercase text-${textColor}/55`}\n >\n {group.date}\n </span>\n <span\n aria-hidden=\"true\"\n className=\"h-px flex-1\"\n style={{\n background: `color-mix(in oklch, var(--color-${textColor}) 8%, transparent)`,\n }}\n />\n </header>\n\n <ul className=\"relative flex flex-col\">\n {/* Single continuous spine — anchored to the first and last\n avatar centers. Avatar is size-9 (36px), inside a li with\n px-2 py-2 (8px padding). So avatar center = 8 + 18 = 26px\n from the edges, which is also where the spine lives. */}\n {showTimeline && group.items.length > 1 && (\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute top-[26px] bottom-[26px] left-[26px] w-px\"\n style={{\n background: `color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n />\n )}\n {group.items.map((activity) => (\n <ActivityRow\n key={activity.id}\n activity={activity}\n accentColor={accentColor}\n textColor={textColor}\n surfaceColor={backgroundColor}\n showRelativeTime={showRelativeTime}\n nowMs={nowMs}\n />\n ))}\n </ul>\n </section>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n\n// ---------- empty state ----------\n\ntype EmptyStateProps = {\n textColor: ColorOptions;\n accentColor: ColorOptions;\n};\n\nfunction EmptyState({ textColor, accentColor }: EmptyStateProps) {\n return (\n <div className=\"flex flex-col items-center justify-center gap-3 px-4 py-10\">\n <div\n className=\"relative flex size-12 items-center justify-center rounded-full\"\n style={{\n background: `color-mix(in oklch, var(--color-${accentColor}) 10%, transparent)`,\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${textColor}) 8%, transparent)`,\n }}\n >\n <AudioWaveform className={`size-5 text-${accentColor}/80`} />\n </div>\n <div className=\"flex flex-col items-center gap-1\">\n <p className={`text-[13px] font-semibold text-${textColor}/80`}>\n No activity yet\n </p>\n <p\n className={`max-w-[24ch] text-center text-[11px] leading-snug text-${textColor}/55`}\n >\n Orders, messages, views and leads will show up here as they happen.\n </p>\n </div>\n </div>\n );\n}\n\n// ---------- skeleton ----------\n\ntype ActivitySkeletonProps = {\n textColor: ColorOptions;\n showTimeline: boolean;\n rowCount: number;\n};\n\nfunction ActivitySkeleton({\n textColor,\n showTimeline,\n rowCount,\n}: ActivitySkeletonProps) {\n return (\n <div className=\"relative flex flex-col\" aria-busy=\"true\">\n {showTimeline && (\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute top-[26px] bottom-[26px] left-[26px] w-px\"\n style={{\n background: `color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n />\n )}\n {Array.from({ length: rowCount }, (_, i) => i).map((i) => (\n <div\n key={i}\n className=\"relative flex items-start gap-3 rounded-md px-2 py-2\"\n >\n <div\n className={`size-9 shrink-0 animate-pulse rounded-full bg-${textColor}/10`}\n />\n <div className=\"flex min-w-0 flex-1 flex-col gap-1.5\">\n <div\n className={`h-2 w-2/3 animate-pulse rounded-full bg-${textColor}/10`}\n />\n <div\n className={`h-2 w-1/2 animate-pulse rounded-full bg-${textColor}/10`}\n />\n </div>\n </div>\n ))}\n </div>\n );\n}\n\n// ---------- schema ----------\n\nexport const recentActivityWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"RecentActivityWidget\",\n displayName: \"Recent Activity Widget\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [\n // Title\n {\n key: \"titleEnabled\",\n label: \"Widget Title\",\n type: \"boolean\",\n description: \"Display the title and eyebrow above the feed\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n },\n {\n key: \"titleText\",\n label: \"Title\",\n type: \"text\",\n description: \"Title text displayed above the activity feed\",\n defaultValue: \"Activity\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n getFontSizeField({\n key: \"titleFontSize\",\n label: \"Title Font Size\",\n description: \"Font size for the widget title\",\n defaultValue: \"xl\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n getColorField({\n key: \"titleColor\",\n label: \"Title Color\",\n description: \"Color for the widget title\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n {\n key: \"showEyebrow\",\n label: \"Show 'RECENT' Eyebrow\",\n type: \"boolean\",\n description: \"Small uppercase label above the title\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n {\n key: \"showCountChip\",\n label: \"Show Updates Count Chip\",\n type: \"boolean\",\n description: \"Accent pill next to the title showing how many updates\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n\n // Feed\n {\n key: \"maxItemsToShow\",\n label: \"Max Items\",\n type: \"number\",\n description: \"Maximum number of activity items to display\",\n defaultValue: DEFAULT_MAX_ITEMS_TO_SHOW,\n min: 1,\n max: MAX_ITEMS_TO_SHOW_LIMIT,\n step: 1,\n tab: \"styling\",\n group: \"Feed\",\n },\n {\n key: \"showTimeline\",\n label: \"Timeline Spine\",\n type: \"boolean\",\n description: \"Draw a vertical line connecting activity avatars\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Feed\",\n },\n {\n key: \"showRelativeTime\",\n label: \"Relative Timestamps\",\n type: \"boolean\",\n description: \"Show 'now / 2m / 1h / 3d' instead of absolute times\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Feed\",\n },\n\n // Design\n {\n type: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Background for the widget container\",\n defaultValue: \"background\",\n tab: \"styling\",\n group: \"Design\",\n },\n getColorField({\n key: \"textColor\",\n label: \"Text Color\",\n description: \"Default text color for activity content\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getColorField({\n key: \"accentColor\",\n label: \"Accent Color\",\n description:\n \"Drives the count chip, type badge, initials avatar, and empty-state icon\",\n defaultValue: \"primary\",\n tab: \"styling\",\n group: \"Design\",\n }),\n {\n key: \"separator\",\n type: \"separator\",\n label: \"Separator\",\n tab: \"styling\",\n group: \"Design\",\n },\n getPaddingField({\n key: \"padding\",\n label: \"Padding\",\n description: \"Padding around the widget container\",\n defaultValue: 4,\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderRadiusField({\n key: \"borderRadius\",\n label: \"Border Radius\",\n description: \"Border radius for the widget container\",\n defaultValue: \"md\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderWidthField({\n key: \"borderWidth\",\n label: \"Border Width\",\n description: \"Border width for the widget container\",\n defaultValue: \"thin\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderColorField({\n key: \"borderColor\",\n label: \"Border Color\",\n description: \"Border color for the widget container\",\n defaultValue: \"muted\",\n tab: \"styling\",\n group: \"Design\",\n }),\n ],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EAGD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,KAAK;GACL,MAAM;GACN,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EAGD;GACE,MAAM;GACN,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACDA,mBAAAA,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACFA,mBAAAA,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aACE;GACF,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF;GACE,KAAK;GACL,MAAM;GACN,OAAO;GACP,KAAK;GACL,OAAO;GACR;EACDC,mBAAAA,gBAAgB;GACd,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACFC,mBAAAA,qBAAqB;GACnB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACFC,mBAAAA,oBAAoB;GAClB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACFC,mBAAAA,oBAAoB;GAClB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACH;CACF"}
|
|
1
|
+
{"version":3,"file":"RecentActivityWidget-DNyhUZNs.cjs","names":["useWidgetsApi","useWidgetPreviewContext","useDataSourceRegistryConfig","ShoppingCart","MessageCircle","Play","UserPlus","Eye","Calendar","Star","CheckSquare","Bell","Trophy","User","borderWidthClasses","borderColorClasses","ErrorState","AudioWaveform","getFontSizeField","getColorField","getPaddingField","getBorderRadiusField","getBorderWidthField","getBorderColorField"],"sources":["../../widgets/src/hooks/use-activities.preview.ts","../../widgets/src/hooks/use-activities.ts","../../widgets/src/widgets/RecentActivityWidget.tsx"],"sourcesContent":["import type { Activity } from \"@fluid-app/portal-core/widgets-api-types\";\n\nconst now = new Date();\n\nfunction minutesAgo(minutes: number): string {\n return new Date(now.getTime() - minutes * 60_000).toISOString();\n}\n\nexport const PREVIEW_DATA: Activity[] = [\n {\n id: 1,\n userName: \"Sarah Johnson\",\n avatarUrl: null,\n activityType: \"Order Placed\",\n targetName: \"Wellness Starter Kit\",\n timestamp: minutesAgo(12),\n slug: \"order_placed\",\n },\n {\n id: 2,\n userName: \"Mike Chen\",\n avatarUrl: null,\n activityType: \"New Lead\",\n targetName: \"Signed up from Instagram link\",\n timestamp: minutesAgo(45),\n slug: \"new_lead\",\n },\n {\n id: 3,\n userName: \"Visitor from Austin, TX\",\n avatarUrl: null,\n activityType: \"Page Views\",\n targetName: \"Viewed product catalog (3 pages)\",\n timestamp: minutesAgo(90),\n slug: \"page_views\",\n },\n {\n id: 4,\n userName: \"Lisa Park\",\n avatarUrl: null,\n activityType: \"Video Complete\",\n targetName: \"Watched: Getting Started Guide\",\n timestamp: minutesAgo(180),\n slug: \"video_complete\",\n },\n];\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useWidgetsApi } from \"@fluid-app/portal-core/widgets-api-context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useDataSourceRegistryConfig } from \"@fluid-app/portal-react/data-sources/registry-context\";\nimport { PREVIEW_DATA } from \"./use-activities.preview\";\nimport type { Activity } from \"@fluid-app/portal-core/widgets-api-types\";\n\nexport type {\n Activity,\n ActivitySlug,\n} from \"@fluid-app/portal-core/widgets-api-types\";\n\nexport function useActivities(): UseQueryResult<Activity[], Error> {\n const widgetsApi = useWidgetsApi();\n const { isPreview } = useWidgetPreviewContext();\n const { baseUrl } = useDataSourceRegistryConfig();\n\n return useQuery({\n queryKey: [\n \"portal-widget-use\",\n \"activities\",\n isPreview ? \"preview\" : baseUrl,\n ] as const,\n queryFn: ({ signal }) => widgetsApi.fetchActivities(signal),\n enabled: !isPreview,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\n });\n}\n","import { useEffect, useMemo, useState, type ComponentProps } from \"react\";\nimport type React from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n BorderWidthOptions,\n ColorOptions,\n FontSizeOptions,\n PaddingOptions,\n} from \"@fluid-app/portal-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport {\n borderColorClasses,\n borderWidthClasses,\n getBorderColorField,\n getBorderRadiusField,\n getBorderWidthField,\n getColorField,\n getFontSizeField,\n getPaddingField,\n} from \"../core/fields\";\nimport {\n useActivities,\n type Activity,\n type ActivitySlug,\n} from \"../hooks/use-activities\";\nimport { ErrorState } from \"../components/error-state\";\nimport {\n AudioWaveform,\n Bell,\n Calendar,\n CheckSquare,\n Eye,\n MessageCircle,\n Play,\n ShoppingCart,\n Star,\n Trophy,\n User,\n UserPlus,\n type LucideIcon,\n} from \"lucide-react\";\n\n// ---------- helpers ----------\n\nconst RELATIVE_TIME_REFRESH_MS = 60_000;\nconst DEFAULT_MAX_ITEMS_TO_SHOW = 6;\nconst MAX_ITEMS_TO_SHOW_LIMIT = 20;\n\nconst ACTIVITY_ICON_MAP: Record<ActivitySlug, LucideIcon> = {\n order_placed: ShoppingCart,\n abandoned_cart: ShoppingCart,\n cart_items_added: ShoppingCart,\n new_cart_items_added: ShoppingCart,\n direct_message: MessageCircle,\n comment_reply: MessageCircle,\n message_received: MessageCircle,\n message_sent: MessageCircle,\n video: Play,\n video_complete: Play,\n video_contact: Play,\n video_complete_contact: Play,\n new_lead: UserPlus,\n page_views_contact: UserPlus,\n smart_link_clicked: UserPlus,\n page_views: Eye,\n upcoming_event: Calendar,\n review_left: Star,\n tasks: CheckSquare,\n announcements: Bell,\n fantasy_point: Trophy,\n};\n\nconst getActivityIcon = (slug: ActivitySlug): LucideIcon =>\n ACTIVITY_ICON_MAP[slug] ?? User;\n\nconst formatAbsoluteTime = (timestamp: string): string =>\n new Date(timestamp).toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true,\n });\n\nconst formatRelativeTime = (timestamp: string, now: number): string => {\n const then = new Date(timestamp).getTime();\n const diffSec = Math.max(0, Math.round((now - then) / 1000));\n\n if (diffSec < 60) return \"now\";\n const diffMin = Math.floor(diffSec / 60);\n if (diffMin < 60) return `${diffMin}m`;\n const diffHr = Math.floor(diffMin / 60);\n if (diffHr < 24) return `${diffHr}h`;\n const diffDay = Math.floor(diffHr / 24);\n if (diffDay < 7) return `${diffDay}d`;\n return new Date(timestamp).toLocaleDateString(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n });\n};\n\nconst toLocalDateKey = (date: Date): string => {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n return `${year}-${month}-${day}`;\n};\n\nconst formatDateHeader = (timestamp: string, nowMs: number): string => {\n const date = new Date(timestamp);\n if (!Number.isFinite(date.getTime())) return timestamp;\n\n const now = new Date(nowMs);\n const yesterday = new Date(now);\n yesterday.setDate(now.getDate() - 1);\n\n const todayKey = toLocalDateKey(now);\n const yesterdayKey = toLocalDateKey(yesterday);\n const dateKey = toLocalDateKey(date);\n\n if (dateKey === todayKey) return \"Today\";\n if (dateKey === yesterdayKey) return \"Yesterday\";\n return date.toLocaleDateString(\"en-US\", {\n month: \"long\",\n day: \"numeric\",\n });\n};\n\nconst getDateKey = (timestamp: string): string => {\n const date = new Date(timestamp);\n if (!Number.isFinite(date.getTime())) return timestamp;\n return toLocalDateKey(date);\n};\n\nconst getColorCssValue = (color: ColorOptions): string =>\n color === \"transparent\" ? \"transparent\" : `var(--color-${color})`;\n\nconst normalizeMaxItemsToShow = (value: number): number => {\n if (!Number.isFinite(value)) return DEFAULT_MAX_ITEMS_TO_SHOW;\n return Math.min(MAX_ITEMS_TO_SHOW_LIMIT, Math.max(1, Math.floor(value)));\n};\n\nconst shouldRenderEyebrow = (\n showEyebrow: boolean,\n titleText: string,\n): boolean =>\n showEyebrow && titleText.trim().toLowerCase() !== \"recent activity\";\n\nconst isUnknownUser = (name: string | undefined): boolean => {\n if (!name) return true;\n const lower = name.trim().toLowerCase();\n if (!lower) return true;\n return (\n lower === \"unknown\" ||\n lower.includes(\"unknown visitor\") ||\n lower === \"anonymous\" ||\n lower === \"guest\"\n );\n};\n\nconst getInitials = (name: string | undefined): string => {\n if (!name) return \"\";\n const parts = name.trim().split(/\\s+/).slice(0, 2);\n return parts.map((p) => p[0]?.toUpperCase() ?? \"\").join(\"\");\n};\n\n// ---------- avatar ----------\n\ntype AvatarProps = {\n activity: Activity;\n textColor: ColorOptions;\n accentColor: ColorOptions;\n surfaceColor: ColorOptions;\n showTypeBadge: boolean;\n};\n\nfunction Avatar({\n activity,\n textColor,\n accentColor,\n surfaceColor,\n showTypeBadge,\n}: AvatarProps) {\n const TypeIcon = getActivityIcon(activity.slug);\n const unknown = isUnknownUser(activity.userName);\n const initials = unknown ? \"\" : getInitials(activity.userName);\n // 3-tier fallback: image → initials → person icon. If avatarUrl 404s or\n // fails to decode, flip to the next tier instead of showing a broken image.\n const [failedAvatarUrl, setFailedAvatarUrl] = useState<string | null>(null);\n const avatarUrl = activity.avatarUrl;\n const showImage = Boolean(avatarUrl) && failedAvatarUrl !== avatarUrl;\n\n return (\n <div className=\"relative shrink-0\">\n {showImage ? (\n <img\n src={avatarUrl ?? undefined}\n alt={unknown ? \"\" : activity.userName}\n width={36}\n height={36}\n loading=\"lazy\"\n onError={() => setFailedAvatarUrl(avatarUrl)}\n className=\"size-9 rounded-full object-cover\"\n style={{\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n />\n ) : initials ? (\n <div\n className={`flex size-9 items-center justify-center rounded-full text-[11px] font-bold tracking-[0.04em] text-${textColor}/80`}\n style={{\n background: `color-mix(in oklch, var(--color-${accentColor}) 12%, transparent)`,\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n aria-label={activity.userName}\n >\n {initials}\n </div>\n ) : (\n <div\n className={`flex size-9 items-center justify-center rounded-full`}\n style={{\n background: `color-mix(in oklch, var(--color-${textColor}) 5%, transparent)`,\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n aria-label=\"Unknown visitor\"\n >\n <User className={`size-3.5 text-${textColor}/55`} />\n </div>\n )}\n\n {/* Type badge — shows what kind of event, regardless of avatar kind. */}\n {showTypeBadge && (\n <span\n className={`bg-${accentColor} text-${accentColor}-foreground absolute -right-0.5 -bottom-0.5 flex size-4 items-center justify-center rounded-full`}\n style={{\n boxShadow: `0 0 0 2px ${getColorCssValue(surfaceColor)}`,\n }}\n aria-hidden=\"true\"\n >\n <TypeIcon style={{ width: 8, height: 8 }} />\n </span>\n )}\n </div>\n );\n}\n\n// ---------- activity row ----------\n\ntype ActivityRowProps = {\n activity: Activity;\n accentColor: ColorOptions;\n textColor: ColorOptions;\n surfaceColor: ColorOptions;\n showRelativeTime: boolean;\n nowMs: number;\n};\n\nfunction ActivityRow({\n activity,\n accentColor,\n textColor,\n surfaceColor,\n showRelativeTime,\n nowMs,\n}: ActivityRowProps) {\n const timeText = showRelativeTime\n ? formatRelativeTime(activity.timestamp, nowMs)\n : formatAbsoluteTime(activity.timestamp);\n\n const absoluteTooltip = new Date(activity.timestamp).toLocaleString(\"en-US\", {\n weekday: \"short\",\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n\n return (\n <li\n role=\"article\"\n className=\"relative flex items-start gap-3 rounded-md px-2 py-2\"\n >\n <Avatar\n activity={activity}\n textColor={textColor}\n accentColor={accentColor}\n surfaceColor={surfaceColor}\n showTypeBadge\n />\n\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <div className=\"flex items-baseline gap-2\">\n <p\n className={`min-w-0 flex-1 truncate text-[13px] leading-tight font-semibold text-${textColor}`}\n >\n {activity.activityType}\n </p>\n <span\n title={absoluteTooltip}\n className={`shrink-0 font-mono text-[10px] font-medium tabular-nums text-${textColor}/45`}\n >\n {timeText}\n </span>\n </div>\n <p\n className={`mt-0.5 truncate text-[12px] leading-snug text-${textColor}/65`}\n >\n <span className={`font-semibold text-${textColor}/85`}>\n {activity.userName}\n </span>\n {activity.targetName && (\n <>\n <span className={`mx-1 text-${textColor}/35`}>·</span>\n <span>{activity.targetName}</span>\n </>\n )}\n </p>\n </div>\n </li>\n );\n}\n\n// ---------- widget ----------\n\ntype RecentActivityWidgetProps = ComponentProps<\"div\"> & {\n titleEnabled?: boolean;\n titleText?: string;\n titleFontSize?: FontSizeOptions;\n titleColor?: ColorOptions;\n\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n borderWidth?: BorderWidthOptions;\n borderColor?: ColorOptions;\n\n maxItemsToShow?: number;\n\n // Polish (additive)\n showTimeline?: boolean;\n showRelativeTime?: boolean;\n showCountChip?: boolean;\n showEyebrow?: boolean;\n};\n\nfunction useRelativeTimeNow(): number {\n const [nowMs, setNowMs] = useState(() => Date.now());\n\n useEffect(() => {\n const timer = window.setInterval(() => {\n setNowMs(Date.now());\n }, RELATIVE_TIME_REFRESH_MS);\n\n return () => window.clearInterval(timer);\n }, []);\n\n return nowMs;\n}\n\nexport function RecentActivityWidget({\n titleEnabled = true,\n titleText = \"Activity\",\n titleFontSize = \"xl\",\n titleColor = \"foreground\",\n\n background = { type: \"solid\", color: \"background\" },\n textColor = \"foreground\",\n accentColor = \"primary\",\n padding = 4,\n borderRadius = \"md\",\n borderWidth = \"thin\",\n borderColor = \"muted\",\n\n maxItemsToShow = 6,\n\n showTimeline = true,\n showRelativeTime = true,\n showCountChip = true,\n showEyebrow = true,\n\n className,\n ...props\n}: RecentActivityWidgetProps): React.JSX.Element {\n const backgroundColor = background.color || \"background\";\n const backgroundImage =\n (background.resource?.image_url || background.resource?.imageUrl) &&\n background.type === \"image\"\n ? `url(${background.resource.image_url || background.resource.imageUrl})`\n : \"none\";\n\n const { data: activities = [], isLoading, isError } = useActivities();\n const nowMs = useRelativeTimeNow();\n const maxVisibleItems = normalizeMaxItemsToShow(maxItemsToShow);\n\n const activitiesToShow = useMemo(() => {\n if (activities.length === 0)\n return [] as { date: string; items: Activity[] }[];\n const groups = new Map<string, Activity[]>();\n for (const activity of activities.slice(0, maxVisibleItems)) {\n const key = getDateKey(activity.timestamp);\n const existing = groups.get(key);\n if (existing) existing.push(activity);\n else groups.set(key, [activity]);\n }\n return Array.from(groups.entries()).map(([key, items]) => ({\n // `key` is only a defensive fallback; every group is created with at\n // least one activity, so the item timestamp is what normally labels it.\n date: formatDateHeader(items[0]?.timestamp ?? key, nowMs),\n items,\n }));\n }, [activities, maxVisibleItems, nowMs]);\n\n const totalCount = Math.min(activities.length, maxVisibleItems);\n\n return (\n <div\n className={`@container overflow-hidden rounded-${borderRadius} bg-${backgroundColor} text-${textColor} ${borderWidthClasses[borderWidth]} ${borderWidth !== \"none\" ? borderColorClasses[borderColor] : \"\"} ${className ?? \"\"}`}\n style={{\n backgroundImage,\n // Soft ambient lift derived from theme tokens. The configured border\n // props own the crisp outline so saved border settings stay intact.\n boxShadow: `0 1px 2px color-mix(in oklch, var(--color-foreground) 4%, transparent), 0 14px 32px -20px color-mix(in oklch, var(--color-foreground) 14%, transparent)`,\n }}\n {...props}\n >\n <div className={`p-${padding} flex flex-col`}>\n {/* Header */}\n {titleEnabled && titleText && (\n <div className=\"mb-4 flex items-end justify-between gap-3\">\n <div className=\"flex min-w-0 flex-col\">\n {shouldRenderEyebrow(showEyebrow, titleText) && (\n <span\n className={`text-[10px] font-bold tracking-[0.2em] uppercase text-${textColor}/55`}\n >\n Recent\n </span>\n )}\n <h2\n className={`mt-0.5 text-${titleFontSize} leading-tight font-bold tracking-[-0.015em] text-${titleColor}`}\n >\n {titleText}\n </h2>\n </div>\n {!isLoading && !isError && showCountChip && totalCount > 0 && (\n <span\n className={`inline-flex shrink-0 items-center gap-1.5 rounded-full bg-${accentColor}/10 px-2 py-1 text-[10px] font-bold tracking-[0.06em] text-${accentColor}`}\n >\n <span\n aria-hidden=\"true\"\n className={`size-1.5 rounded-full bg-${accentColor}`}\n />\n {totalCount} {totalCount === 1 ? \"update\" : \"updates\"}\n </span>\n )}\n </div>\n )}\n\n {/* States */}\n {isLoading ? (\n <ActivitySkeleton\n textColor={textColor}\n showTimeline={showTimeline}\n rowCount={maxVisibleItems}\n />\n ) : isError ? (\n <ErrorState />\n ) : activities.length === 0 ? (\n <EmptyState textColor={textColor} accentColor={accentColor} />\n ) : (\n /* Feed */\n <div role=\"feed\" className=\"flex flex-col gap-3\">\n {activitiesToShow.map((group, groupIdx) => (\n <section\n key={`${group.date}-${groupIdx}`}\n className=\"flex flex-col gap-1\"\n >\n <header className=\"flex items-center gap-2 px-2\">\n <span\n className={`text-[10px] font-bold tracking-[0.18em] uppercase text-${textColor}/55`}\n >\n {group.date}\n </span>\n <span\n aria-hidden=\"true\"\n className=\"h-px flex-1\"\n style={{\n background: `color-mix(in oklch, var(--color-${textColor}) 8%, transparent)`,\n }}\n />\n </header>\n\n <ul className=\"relative flex flex-col\">\n {/* Single continuous spine — anchored to the first and last\n avatar centers. Avatar is size-9 (36px), inside a li with\n px-2 py-2 (8px padding). So avatar center = 8 + 18 = 26px\n from the edges, which is also where the spine lives. */}\n {showTimeline && group.items.length > 1 && (\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute top-[26px] bottom-[26px] left-[26px] w-px\"\n style={{\n background: `color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n />\n )}\n {group.items.map((activity) => (\n <ActivityRow\n key={activity.id}\n activity={activity}\n accentColor={accentColor}\n textColor={textColor}\n surfaceColor={backgroundColor}\n showRelativeTime={showRelativeTime}\n nowMs={nowMs}\n />\n ))}\n </ul>\n </section>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n\n// ---------- empty state ----------\n\ntype EmptyStateProps = {\n textColor: ColorOptions;\n accentColor: ColorOptions;\n};\n\nfunction EmptyState({ textColor, accentColor }: EmptyStateProps) {\n return (\n <div className=\"flex flex-col items-center justify-center gap-3 px-4 py-10\">\n <div\n className=\"relative flex size-12 items-center justify-center rounded-full\"\n style={{\n background: `color-mix(in oklch, var(--color-${accentColor}) 10%, transparent)`,\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${textColor}) 8%, transparent)`,\n }}\n >\n <AudioWaveform className={`size-5 text-${accentColor}/80`} />\n </div>\n <div className=\"flex flex-col items-center gap-1\">\n <p className={`text-[13px] font-semibold text-${textColor}/80`}>\n No activity yet\n </p>\n <p\n className={`max-w-[24ch] text-center text-[11px] leading-snug text-${textColor}/55`}\n >\n Orders, messages, views and leads will show up here as they happen.\n </p>\n </div>\n </div>\n );\n}\n\n// ---------- skeleton ----------\n\ntype ActivitySkeletonProps = {\n textColor: ColorOptions;\n showTimeline: boolean;\n rowCount: number;\n};\n\nfunction ActivitySkeleton({\n textColor,\n showTimeline,\n rowCount,\n}: ActivitySkeletonProps) {\n return (\n <div className=\"relative flex flex-col\" aria-busy=\"true\">\n {showTimeline && (\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute top-[26px] bottom-[26px] left-[26px] w-px\"\n style={{\n background: `color-mix(in oklch, var(--color-${textColor}) 10%, transparent)`,\n }}\n />\n )}\n {Array.from({ length: rowCount }, (_, i) => i).map((i) => (\n <div\n key={i}\n className=\"relative flex items-start gap-3 rounded-md px-2 py-2\"\n >\n <div\n className={`size-9 shrink-0 animate-pulse rounded-full bg-${textColor}/10`}\n />\n <div className=\"flex min-w-0 flex-1 flex-col gap-1.5\">\n <div\n className={`h-2 w-2/3 animate-pulse rounded-full bg-${textColor}/10`}\n />\n <div\n className={`h-2 w-1/2 animate-pulse rounded-full bg-${textColor}/10`}\n />\n </div>\n </div>\n ))}\n </div>\n );\n}\n\n// ---------- schema ----------\n\nexport const recentActivityWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"RecentActivityWidget\",\n displayName: \"Recent Activity Widget\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [\n // Title\n {\n key: \"titleEnabled\",\n label: \"Widget Title\",\n type: \"boolean\",\n description: \"Display the title and eyebrow above the feed\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n },\n {\n key: \"titleText\",\n label: \"Title\",\n type: \"text\",\n description: \"Title text displayed above the activity feed\",\n defaultValue: \"Activity\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n getFontSizeField({\n key: \"titleFontSize\",\n label: \"Title Font Size\",\n description: \"Font size for the widget title\",\n defaultValue: \"xl\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n getColorField({\n key: \"titleColor\",\n label: \"Title Color\",\n description: \"Color for the widget title\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n {\n key: \"showEyebrow\",\n label: \"Show 'RECENT' Eyebrow\",\n type: \"boolean\",\n description: \"Small uppercase label above the title\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n {\n key: \"showCountChip\",\n label: \"Show Updates Count Chip\",\n type: \"boolean\",\n description: \"Accent pill next to the title showing how many updates\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n\n // Feed\n {\n key: \"maxItemsToShow\",\n label: \"Max Items\",\n type: \"number\",\n description: \"Maximum number of activity items to display\",\n defaultValue: DEFAULT_MAX_ITEMS_TO_SHOW,\n min: 1,\n max: MAX_ITEMS_TO_SHOW_LIMIT,\n step: 1,\n tab: \"styling\",\n group: \"Feed\",\n },\n {\n key: \"showTimeline\",\n label: \"Timeline Spine\",\n type: \"boolean\",\n description: \"Draw a vertical line connecting activity avatars\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Feed\",\n },\n {\n key: \"showRelativeTime\",\n label: \"Relative Timestamps\",\n type: \"boolean\",\n description: \"Show 'now / 2m / 1h / 3d' instead of absolute times\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Feed\",\n },\n\n // Design\n {\n type: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Background for the widget container\",\n defaultValue: \"background\",\n tab: \"styling\",\n group: \"Design\",\n },\n getColorField({\n key: \"textColor\",\n label: \"Text Color\",\n description: \"Default text color for activity content\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getColorField({\n key: \"accentColor\",\n label: \"Accent Color\",\n description:\n \"Drives the count chip, type badge, initials avatar, and empty-state icon\",\n defaultValue: \"primary\",\n tab: \"styling\",\n group: \"Design\",\n }),\n {\n key: \"separator\",\n type: \"separator\",\n label: \"Separator\",\n tab: \"styling\",\n group: \"Design\",\n },\n getPaddingField({\n key: \"padding\",\n label: \"Padding\",\n description: \"Padding around the widget container\",\n defaultValue: 4,\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderRadiusField({\n key: \"borderRadius\",\n label: \"Border Radius\",\n description: \"Border radius for the widget container\",\n defaultValue: \"md\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderWidthField({\n key: \"borderWidth\",\n label: \"Border Width\",\n description: \"Border width for the widget container\",\n defaultValue: \"thin\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderColorField({\n key: \"borderColor\",\n label: \"Border Color\",\n description: \"Border color for the widget container\",\n defaultValue: \"muted\",\n tab: \"styling\",\n group: \"Design\",\n }),\n ],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EAGD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,KAAK;GACL,MAAM;GACN,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EAGD;GACE,MAAM;GACN,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACDA,mBAAAA,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACFA,mBAAAA,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aACE;GACF,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF;GACE,KAAK;GACL,MAAM;GACN,OAAO;GACP,KAAK;GACL,OAAO;GACR;EACDC,mBAAAA,gBAAgB;GACd,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACFC,mBAAAA,qBAAqB;GACnB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACFC,mBAAAA,oBAAoB;GAClB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACFC,mBAAAA,oBAAoB;GAClB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACH;CACF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as useDataSourceRegistryConfig } from "./registry-context-
|
|
1
|
+
import { n as useDataSourceRegistryConfig } from "./registry-context-CTHUCfEc.mjs";
|
|
2
2
|
import { createContext, memo, useCallback, useContext, useMemo, useRef } from "react";
|
|
3
3
|
import { useQueries } from "@tanstack/react-query";
|
|
4
4
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -671,4 +671,4 @@ function ScreenRenderer(props) {
|
|
|
671
671
|
//#endregion
|
|
672
672
|
export { assertNever as a, useScreenRenderer as c, DataSourceApiProvider as d, assertDefined as i, RegistryProvider as l, DataAwareWidget as n, isWidgetType as o, WIDGET_TYPE_NAMES as r, isWidgetTypeName as s, ScreenRenderer as t, useRegistry as u };
|
|
673
673
|
|
|
674
|
-
//# sourceMappingURL=ScreenRenderer-
|
|
674
|
+
//# sourceMappingURL=ScreenRenderer-Cl2aAJ7D.mjs.map
|