@fluid-app/rep-widgets 0.1.17 → 0.1.18
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-Dk8O2NRC.mjs → CalendarWidget-DshBk5tG.mjs} +2 -2
- package/dist/CalendarWidget-DshBk5tG.mjs.map +1 -0
- package/dist/{CalendarWidget-BI8sv5HJ.cjs → CalendarWidget-s7KgQ5JD.cjs} +2 -2
- package/dist/CalendarWidget-s7KgQ5JD.cjs.map +1 -0
- package/dist/{RecentActivityWidget-ComwTgR9.mjs → RecentActivityWidget-CxEgRz3c.mjs} +2 -2
- package/dist/RecentActivityWidget-CxEgRz3c.mjs.map +1 -0
- package/dist/{RecentActivityWidget-BgsjgQY5.cjs → RecentActivityWidget-u1zCrwGF.cjs} +2 -2
- package/dist/RecentActivityWidget-u1zCrwGF.cjs.map +1 -0
- package/dist/widgets/index.cjs +4 -4
- package/dist/widgets/index.mjs +4 -4
- package/package.json +3 -3
- package/dist/CalendarWidget-BI8sv5HJ.cjs.map +0 -1
- package/dist/CalendarWidget-Dk8O2NRC.mjs.map +0 -1
- package/dist/RecentActivityWidget-BgsjgQY5.cjs.map +0 -1
- package/dist/RecentActivityWidget-ComwTgR9.mjs.map +0 -1
|
@@ -61,7 +61,7 @@ function useCalendarEvents() {
|
|
|
61
61
|
isPreview ? "preview" : baseUrl
|
|
62
62
|
],
|
|
63
63
|
queryFn: async ({ signal }) => {
|
|
64
|
-
const url = baseUrl ? `${baseUrl}/
|
|
64
|
+
const url = baseUrl ? `${baseUrl}/events.json?event_timeline=upcoming` : "/events.json?event_timeline=upcoming";
|
|
65
65
|
const response = await fetch(url, {
|
|
66
66
|
headers: {
|
|
67
67
|
"content-type": "application/json",
|
|
@@ -421,4 +421,4 @@ const calendarWidgetPropertySchema = {
|
|
|
421
421
|
//#endregion
|
|
422
422
|
export { CalendarWidget_exports as n, calendarWidgetPropertySchema as r, CalendarWidget as t };
|
|
423
423
|
|
|
424
|
-
//# sourceMappingURL=CalendarWidget-
|
|
424
|
+
//# sourceMappingURL=CalendarWidget-DshBk5tG.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CalendarWidget-DshBk5tG.mjs","names":[],"sources":["../src/hooks/use-calendar-events.preview.ts","../src/hooks/use-calendar-events.ts","../src/widgets/CalendarWidget.tsx"],"sourcesContent":["import type { CalendarEvent } from \"./use-calendar-events.types\";\n\nconst today = new Date();\nconst inTwoDays = new Date(today);\ninTwoDays.setDate(inTwoDays.getDate() + 2);\n\nfunction toISO(date: Date, hours: number, minutes = 0): string {\n const d = new Date(date);\n d.setHours(hours, minutes, 0, 0);\n return d.toISOString();\n}\n\nfunction toDateString(date: Date): string {\n return date.toISOString().split(\"T\")[0]!;\n}\n\nexport const PREVIEW_DATA: CalendarEvent[] = [\n {\n id: 1,\n title: \"Team Strategy Call\",\n start: toISO(today, 9, 0),\n end: toISO(today, 10, 0),\n status: \"active\",\n color: \"#4f46e5\",\n },\n {\n id: 2,\n title: \"Product Launch Webinar\",\n start: toISO(today, 14, 0),\n end: toISO(today, 15, 30),\n status: \"active\",\n color: \"#0891b2\",\n venue: \"Zoom\",\n },\n {\n id: 3,\n title: \"Annual Leadership Summit\",\n start: `${toDateString(inTwoDays)}T00:00:00.000Z`,\n end: `${toDateString(inTwoDays)}T23:59:59.000Z`,\n status: \"active\",\n color: \"#059669\",\n isAllDay: true,\n venue: \"Convention Center\",\n },\n];\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useDataSourceConfig } from \"@fluid-app/rep-core/data-sources/context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/rep-core/data-sources/preview-context\";\nimport { PREVIEW_DATA } from \"./use-calendar-events.preview\";\n\nimport type { CalendarEvent } from \"./use-calendar-events.types\";\n\nexport type { CalendarEvent } from \"./use-calendar-events.types\";\n\nexport function useCalendarEvents() {\n const { baseUrl, getApiHeaders } = useDataSourceConfig();\n const { isPreview } = useWidgetPreviewContext();\n\n return useQuery({\n queryKey: [\n \"rep-widget-use\",\n \"calendar-events\",\n isPreview ? \"preview\" : baseUrl,\n ] as const,\n queryFn: async ({ signal }): Promise<CalendarEvent[]> => {\n const url = baseUrl\n ? `${baseUrl}/events.json?event_timeline=upcoming`\n : \"/events.json?event_timeline=upcoming\";\n const response = await fetch(url, {\n headers: {\n \"content-type\": \"application/json\",\n ...getApiHeaders?.(),\n },\n signal,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch calendar events: ${response.status}`);\n }\n\n return response.json();\n },\n enabled: !isPreview,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\n });\n}\n","import { useState, useMemo, type ComponentProps } from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n FontSizeOptions,\n PaddingOptions,\n} from \"@fluid-app/rep-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/rep-core/registries\";\nimport {\n getBorderRadiusField,\n getColorField,\n getFontSizeField,\n getPaddingField,\n} from \"../core/fields\";\nimport {\n useCalendarEvents,\n type CalendarEvent,\n} from \"../hooks/use-calendar-events\";\nimport { ErrorState } from \"../components/error-state\";\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport {\n faChevronLeft,\n faChevronRight,\n faChevronsDown,\n faChevronsUp,\n} from \"@fortawesome/pro-regular-svg-icons\";\n\n// Utility to get days in a month\nconst getDaysInMonth = (year: number, month: number) => {\n return new Date(year, month + 1, 0).getDate();\n};\n\n// Utility to get the first day of month (0 = Sunday, 1 = Monday, etc.)\nconst getFirstDayOfMonth = (year: number, month: number) => {\n return new Date(year, month, 1).getDay();\n};\n\n// Generate calendar grid for a month\nconst generateMonthGrid = (year: number, month: number) => {\n const daysInMonth = getDaysInMonth(year, month);\n const firstDay = getFirstDayOfMonth(year, month);\n const today = new Date();\n\n const days: Array<{\n date: Date | null;\n isToday: boolean;\n isCurrentMonth: boolean;\n }> = [];\n\n // Add empty slots for days before the 1st\n for (let i = 0; i < firstDay; i++) {\n days.push({ date: null, isToday: false, isCurrentMonth: false });\n }\n\n // Add days of the month\n for (let day = 1; day <= daysInMonth; day++) {\n const date = new Date(year, month, day);\n const isToday =\n date.getDate() === today.getDate() &&\n date.getMonth() === today.getMonth() &&\n date.getFullYear() === today.getFullYear();\n days.push({ date, isToday, isCurrentMonth: true });\n }\n\n return days;\n};\n\n// Generate week grid for current week\nconst generateWeekGrid = () => {\n const today = new Date();\n const currentDay = today.getDay();\n const days: Array<{\n date: Date;\n isToday: boolean;\n }> = [];\n\n for (let i = 0; i < 7; i++) {\n const date = new Date(today);\n date.setDate(today.getDate() - currentDay + i);\n days.push({\n date,\n isToday: date.toDateString() === today.toDateString(),\n });\n }\n\n return days;\n};\n\n// Get events for a specific date\nconst getEventsForDate = (date: Date, events: CalendarEvent[]) => {\n const dayStart = new Date(date);\n dayStart.setHours(0, 0, 0, 0);\n const dayEnd = new Date(date);\n dayEnd.setHours(23, 59, 59, 999);\n\n return events.filter((event) => {\n const eventStart = new Date(event.start);\n const eventEnd = new Date(event.end);\n return eventStart <= dayEnd && eventEnd >= dayStart;\n });\n};\n\n// Check if a date has events\nconst hasEventsOnDate = (date: Date | null, events: CalendarEvent[]) => {\n if (!date) return false;\n return getEventsForDate(date, events).length > 0;\n};\n\n// Event item component\nconst EventItem = ({\n title,\n start,\n end,\n color,\n isAllDay,\n accentColor,\n}: CalendarEvent & { accentColor?: string }) => {\n const startTime = new Date(start).toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n const endTime = new Date(end).toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n\n return (\n <div className=\"flex gap-x-2\">\n <div\n className=\"w-0.5 shrink-0 rounded-full\"\n style={{ backgroundColor: color || accentColor }}\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-sm font-medium\">{title}</div>\n <div className=\"text-xs opacity-60\">\n {isAllDay ? \"All day\" : `${startTime} - ${endTime}`}\n </div>\n </div>\n </div>\n );\n};\n\nconst dayNames = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\n\ntype DayButtonProps = {\n date: Date;\n isToday: boolean;\n isSelected: boolean;\n hasEvents: boolean;\n accentColor: string;\n onClick: () => void;\n extraClassName?: string;\n};\n\nfunction DayButton({\n date,\n isToday,\n isSelected,\n hasEvents,\n accentColor,\n onClick,\n extraClassName = \"\",\n}: DayButtonProps) {\n return (\n <button\n onClick={onClick}\n className={`relative flex aspect-square items-center justify-center rounded-full text-sm transition-colors ${\n isSelected\n ? `bg-${accentColor} text-${accentColor}-foreground`\n : isToday\n ? `bg-${accentColor}/30 text-${accentColor}-foreground hover:bg-${accentColor}/50`\n : `hover:bg-${accentColor}/50 hover:text-${accentColor}-foreground`\n } ${extraClassName}`}\n >\n {date.getDate()}\n {hasEvents && (\n <div\n className={`absolute bottom-1 h-1 w-1 rounded-full bg-${accentColor}`}\n />\n )}\n </button>\n );\n}\n\ntype CalendarWidgetProps = ComponentProps<\"div\"> & {\n // Title\n titleEnabled?: boolean;\n titleText?: string;\n titleFontSize?: FontSizeOptions;\n titleColor?: ColorOptions;\n\n // Styling\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n};\n\nexport function CalendarWidget({\n // Title defaults\n titleEnabled = true,\n titleText = \"Calendar\",\n titleFontSize = \"xl\",\n titleColor = \"foreground\",\n\n // Styling defaults\n background = {\n type: \"solid\",\n color: \"background\",\n },\n textColor = \"foreground\",\n accentColor = \"primary\",\n padding = 4,\n borderRadius = \"md\",\n\n className,\n ...props\n}: CalendarWidgetProps) {\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 const today = new Date();\n const [currentMonth, setCurrentMonth] = useState(today.getMonth());\n const [currentYear, setCurrentYear] = useState(today.getFullYear());\n const [isCollapsed, setIsCollapsed] = useState(false);\n const [selectedDate, setSelectedDate] = useState<Date | null>(today);\n\n const { data: events = [], isLoading, isError } = useCalendarEvents();\n\n // Generate calendar grids\n const monthGrid = useMemo(\n () => generateMonthGrid(currentYear, currentMonth),\n [currentYear, currentMonth],\n );\n const weekGrid = useMemo(() => generateWeekGrid(), []);\n\n // Get events for selected date\n const selectedDateEvents = useMemo(() => {\n if (!selectedDate) return [];\n return getEventsForDate(selectedDate, events);\n }, [selectedDate, events]);\n\n // Navigation handlers\n const goToPrevMonth = () => {\n if (currentMonth === 0) {\n setCurrentMonth(11);\n setCurrentYear(currentYear - 1);\n } else {\n setCurrentMonth(currentMonth - 1);\n }\n };\n\n const goToNextMonth = () => {\n if (currentMonth === 11) {\n setCurrentMonth(0);\n setCurrentYear(currentYear + 1);\n } else {\n setCurrentMonth(currentMonth + 1);\n }\n };\n\n const monthYearLabel = new Date(currentYear, currentMonth).toLocaleString(\n \"en-US\",\n {\n month: \"long\",\n year: \"numeric\",\n },\n );\n\n return (\n <div\n className={`@container overflow-hidden rounded-${borderRadius} bg-${backgroundColor} text-${textColor} ${className}`}\n style={{ backgroundImage }}\n {...props}\n >\n {/* Title */}\n {titleEnabled && titleText && (\n <div className={`p-${padding} pb-0`}>\n <h2 className={`text-${titleFontSize} font-bold text-${titleColor}`}>\n {titleText}\n </h2>\n </div>\n )}\n\n {/* Loading state */}\n {isLoading ? (\n <div\n className={`flex min-h-[300px] items-center justify-center p-${padding}`}\n >\n <div className=\"h-8 w-8 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n </div>\n ) : isError ? (\n /* Error state */\n <ErrorState />\n ) : (\n <div className={`p-${padding}`}>\n {/* Desktop: Side by side layout */}\n <div className=\"@md:flex @md:gap-6\">\n {/* Calendar Section */}\n <div className=\"@md:flex-none\">\n {/* Month/Year Header with Navigation */}\n <div className=\"mb-4 flex items-center justify-between\">\n <button\n onClick={goToPrevMonth}\n className=\"hover:bg-foreground/10 rounded-full p-1\"\n aria-label=\"Previous month\"\n >\n <FontAwesomeIcon icon={faChevronLeft} />\n </button>\n\n <span className=\"text-base font-semibold\">\n {monthYearLabel}\n </span>\n\n <button\n onClick={goToNextMonth}\n className=\"hover:bg-foreground/10 rounded-full p-1\"\n aria-label=\"Next month\"\n >\n <FontAwesomeIcon icon={faChevronRight} />\n </button>\n </div>\n\n {/* Day Headers */}\n <div className=\"mb-2 grid grid-cols-7 gap-1 text-center\">\n {dayNames.map((day, idx) => (\n <div\n key={idx}\n className=\"text-xs font-medium uppercase opacity-60\"\n >\n {day}\n </div>\n ))}\n </div>\n\n {/* Calendar Grid - Week view on mobile when collapsed */}\n {isCollapsed && (\n <div className=\"grid grid-cols-7 gap-1 @md:hidden\">\n {weekGrid.map(({ date, isToday }, idx) => (\n <DayButton\n key={idx}\n date={date}\n isToday={isToday}\n isSelected={\n selectedDate?.toDateString() === date.toDateString()\n }\n hasEvents={hasEventsOnDate(date, events)}\n accentColor={accentColor}\n onClick={() => setSelectedDate(date)}\n />\n ))}\n </div>\n )}\n\n {/* Month view (always on desktop, when not collapsed on mobile) */}\n <div\n className={`grid grid-cols-7 gap-1 ${isCollapsed ? \"hidden @md:grid\" : \"grid\"}`}\n >\n {monthGrid.map((day, idx) => {\n if (!day.date) {\n return <div key={idx} className=\"aspect-square\" />;\n }\n\n return (\n <DayButton\n key={idx}\n date={day.date}\n isToday={day.isToday}\n isSelected={\n selectedDate?.toDateString() === day.date.toDateString()\n }\n hasEvents={hasEventsOnDate(day.date, events)}\n accentColor={accentColor}\n onClick={() => setSelectedDate(day.date)}\n extraClassName=\"p-2 @md:m-3\"\n />\n );\n })}\n </div>\n\n {/* Mobile collapse toggle */}\n <button\n onClick={() => setIsCollapsed(!isCollapsed)}\n className={`ml-2 flex w-full items-center justify-center gap-3 rounded-full p-1 text-${textColor}/50 @md:hidden`}\n aria-label={\n isCollapsed ? \"Expand calendar\" : \"Collapse calendar\"\n }\n >\n <div className={`h-1 w-1/2 bg-${textColor}/50`} />\n {isCollapsed ? (\n <FontAwesomeIcon icon={faChevronsDown} />\n ) : (\n <FontAwesomeIcon icon={faChevronsUp} />\n )}\n <div className={`h-1 w-1/2 bg-${textColor}/50`} />\n </button>\n </div>\n\n {/* Events Panel */}\n <div className=\"border-muted mt-4 pt-4 @md:mt-0 @md:w-56 @md:border-l @md:pt-0 @md:pl-6\">\n <div className=\"mb-2 text-sm font-medium opacity-60\">\n {selectedDate\n ? selectedDate.toLocaleDateString(\"en-US\", {\n weekday: \"long\",\n month: \"short\",\n day: \"numeric\",\n })\n : \"Select a date\"}\n </div>\n\n {selectedDateEvents.length > 0 ? (\n <div className=\"flex flex-col gap-3\">\n {selectedDateEvents.map((event, idx) => (\n <EventItem\n key={`${event.id}-${idx}`}\n {...event}\n accentColor={`var(--${accentColor})`}\n />\n ))}\n </div>\n ) : (\n <div className=\"text-sm opacity-50\">No events</div>\n )}\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport const calendarWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"CalendarWidget\",\n displayName: \"Calendar Widget\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [\n // Styling Tab - Title Group\n {\n key: \"titleEnabled\",\n label: \"Widget Title\",\n type: \"boolean\",\n description: \"Enable the title displayed above the calendar\",\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 calendar\",\n defaultValue: \"Calendar\",\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 // Styling Tab - Design Group\n {\n type: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Background for the calendar 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 calendar content\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getColorField({\n key: \"accentColor\",\n label: \"Accent Color\",\n description: \"Color used for today highlight and event indicators\",\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 calendar 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 calendar container\",\n defaultValue: \"md\",\n tab: \"styling\",\n group: \"Design\",\n }),\n ],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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"}
|
|
@@ -61,7 +61,7 @@ function useCalendarEvents() {
|
|
|
61
61
|
isPreview ? "preview" : baseUrl
|
|
62
62
|
],
|
|
63
63
|
queryFn: async ({ signal }) => {
|
|
64
|
-
const url = baseUrl ? `${baseUrl}/
|
|
64
|
+
const url = baseUrl ? `${baseUrl}/events.json?event_timeline=upcoming` : "/events.json?event_timeline=upcoming";
|
|
65
65
|
const response = await fetch(url, {
|
|
66
66
|
headers: {
|
|
67
67
|
"content-type": "application/json",
|
|
@@ -438,4 +438,4 @@ Object.defineProperty(exports, "calendarWidgetPropertySchema", {
|
|
|
438
438
|
}
|
|
439
439
|
});
|
|
440
440
|
|
|
441
|
-
//# sourceMappingURL=CalendarWidget-
|
|
441
|
+
//# sourceMappingURL=CalendarWidget-s7KgQ5JD.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CalendarWidget-s7KgQ5JD.cjs","names":["ErrorState","FontAwesomeIcon","faChevronLeft","faChevronRight","faChevronsDown","faChevronsUp"],"sources":["../src/hooks/use-calendar-events.preview.ts","../src/hooks/use-calendar-events.ts","../src/widgets/CalendarWidget.tsx"],"sourcesContent":["import type { CalendarEvent } from \"./use-calendar-events.types\";\n\nconst today = new Date();\nconst inTwoDays = new Date(today);\ninTwoDays.setDate(inTwoDays.getDate() + 2);\n\nfunction toISO(date: Date, hours: number, minutes = 0): string {\n const d = new Date(date);\n d.setHours(hours, minutes, 0, 0);\n return d.toISOString();\n}\n\nfunction toDateString(date: Date): string {\n return date.toISOString().split(\"T\")[0]!;\n}\n\nexport const PREVIEW_DATA: CalendarEvent[] = [\n {\n id: 1,\n title: \"Team Strategy Call\",\n start: toISO(today, 9, 0),\n end: toISO(today, 10, 0),\n status: \"active\",\n color: \"#4f46e5\",\n },\n {\n id: 2,\n title: \"Product Launch Webinar\",\n start: toISO(today, 14, 0),\n end: toISO(today, 15, 30),\n status: \"active\",\n color: \"#0891b2\",\n venue: \"Zoom\",\n },\n {\n id: 3,\n title: \"Annual Leadership Summit\",\n start: `${toDateString(inTwoDays)}T00:00:00.000Z`,\n end: `${toDateString(inTwoDays)}T23:59:59.000Z`,\n status: \"active\",\n color: \"#059669\",\n isAllDay: true,\n venue: \"Convention Center\",\n },\n];\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useDataSourceConfig } from \"@fluid-app/rep-core/data-sources/context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/rep-core/data-sources/preview-context\";\nimport { PREVIEW_DATA } from \"./use-calendar-events.preview\";\n\nimport type { CalendarEvent } from \"./use-calendar-events.types\";\n\nexport type { CalendarEvent } from \"./use-calendar-events.types\";\n\nexport function useCalendarEvents() {\n const { baseUrl, getApiHeaders } = useDataSourceConfig();\n const { isPreview } = useWidgetPreviewContext();\n\n return useQuery({\n queryKey: [\n \"rep-widget-use\",\n \"calendar-events\",\n isPreview ? \"preview\" : baseUrl,\n ] as const,\n queryFn: async ({ signal }): Promise<CalendarEvent[]> => {\n const url = baseUrl\n ? `${baseUrl}/events.json?event_timeline=upcoming`\n : \"/events.json?event_timeline=upcoming\";\n const response = await fetch(url, {\n headers: {\n \"content-type\": \"application/json\",\n ...getApiHeaders?.(),\n },\n signal,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch calendar events: ${response.status}`);\n }\n\n return response.json();\n },\n enabled: !isPreview,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\n });\n}\n","import { useState, useMemo, type ComponentProps } from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n FontSizeOptions,\n PaddingOptions,\n} from \"@fluid-app/rep-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/rep-core/registries\";\nimport {\n getBorderRadiusField,\n getColorField,\n getFontSizeField,\n getPaddingField,\n} from \"../core/fields\";\nimport {\n useCalendarEvents,\n type CalendarEvent,\n} from \"../hooks/use-calendar-events\";\nimport { ErrorState } from \"../components/error-state\";\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport {\n faChevronLeft,\n faChevronRight,\n faChevronsDown,\n faChevronsUp,\n} from \"@fortawesome/pro-regular-svg-icons\";\n\n// Utility to get days in a month\nconst getDaysInMonth = (year: number, month: number) => {\n return new Date(year, month + 1, 0).getDate();\n};\n\n// Utility to get the first day of month (0 = Sunday, 1 = Monday, etc.)\nconst getFirstDayOfMonth = (year: number, month: number) => {\n return new Date(year, month, 1).getDay();\n};\n\n// Generate calendar grid for a month\nconst generateMonthGrid = (year: number, month: number) => {\n const daysInMonth = getDaysInMonth(year, month);\n const firstDay = getFirstDayOfMonth(year, month);\n const today = new Date();\n\n const days: Array<{\n date: Date | null;\n isToday: boolean;\n isCurrentMonth: boolean;\n }> = [];\n\n // Add empty slots for days before the 1st\n for (let i = 0; i < firstDay; i++) {\n days.push({ date: null, isToday: false, isCurrentMonth: false });\n }\n\n // Add days of the month\n for (let day = 1; day <= daysInMonth; day++) {\n const date = new Date(year, month, day);\n const isToday =\n date.getDate() === today.getDate() &&\n date.getMonth() === today.getMonth() &&\n date.getFullYear() === today.getFullYear();\n days.push({ date, isToday, isCurrentMonth: true });\n }\n\n return days;\n};\n\n// Generate week grid for current week\nconst generateWeekGrid = () => {\n const today = new Date();\n const currentDay = today.getDay();\n const days: Array<{\n date: Date;\n isToday: boolean;\n }> = [];\n\n for (let i = 0; i < 7; i++) {\n const date = new Date(today);\n date.setDate(today.getDate() - currentDay + i);\n days.push({\n date,\n isToday: date.toDateString() === today.toDateString(),\n });\n }\n\n return days;\n};\n\n// Get events for a specific date\nconst getEventsForDate = (date: Date, events: CalendarEvent[]) => {\n const dayStart = new Date(date);\n dayStart.setHours(0, 0, 0, 0);\n const dayEnd = new Date(date);\n dayEnd.setHours(23, 59, 59, 999);\n\n return events.filter((event) => {\n const eventStart = new Date(event.start);\n const eventEnd = new Date(event.end);\n return eventStart <= dayEnd && eventEnd >= dayStart;\n });\n};\n\n// Check if a date has events\nconst hasEventsOnDate = (date: Date | null, events: CalendarEvent[]) => {\n if (!date) return false;\n return getEventsForDate(date, events).length > 0;\n};\n\n// Event item component\nconst EventItem = ({\n title,\n start,\n end,\n color,\n isAllDay,\n accentColor,\n}: CalendarEvent & { accentColor?: string }) => {\n const startTime = new Date(start).toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n const endTime = new Date(end).toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n\n return (\n <div className=\"flex gap-x-2\">\n <div\n className=\"w-0.5 shrink-0 rounded-full\"\n style={{ backgroundColor: color || accentColor }}\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-sm font-medium\">{title}</div>\n <div className=\"text-xs opacity-60\">\n {isAllDay ? \"All day\" : `${startTime} - ${endTime}`}\n </div>\n </div>\n </div>\n );\n};\n\nconst dayNames = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\n\ntype DayButtonProps = {\n date: Date;\n isToday: boolean;\n isSelected: boolean;\n hasEvents: boolean;\n accentColor: string;\n onClick: () => void;\n extraClassName?: string;\n};\n\nfunction DayButton({\n date,\n isToday,\n isSelected,\n hasEvents,\n accentColor,\n onClick,\n extraClassName = \"\",\n}: DayButtonProps) {\n return (\n <button\n onClick={onClick}\n className={`relative flex aspect-square items-center justify-center rounded-full text-sm transition-colors ${\n isSelected\n ? `bg-${accentColor} text-${accentColor}-foreground`\n : isToday\n ? `bg-${accentColor}/30 text-${accentColor}-foreground hover:bg-${accentColor}/50`\n : `hover:bg-${accentColor}/50 hover:text-${accentColor}-foreground`\n } ${extraClassName}`}\n >\n {date.getDate()}\n {hasEvents && (\n <div\n className={`absolute bottom-1 h-1 w-1 rounded-full bg-${accentColor}`}\n />\n )}\n </button>\n );\n}\n\ntype CalendarWidgetProps = ComponentProps<\"div\"> & {\n // Title\n titleEnabled?: boolean;\n titleText?: string;\n titleFontSize?: FontSizeOptions;\n titleColor?: ColorOptions;\n\n // Styling\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n};\n\nexport function CalendarWidget({\n // Title defaults\n titleEnabled = true,\n titleText = \"Calendar\",\n titleFontSize = \"xl\",\n titleColor = \"foreground\",\n\n // Styling defaults\n background = {\n type: \"solid\",\n color: \"background\",\n },\n textColor = \"foreground\",\n accentColor = \"primary\",\n padding = 4,\n borderRadius = \"md\",\n\n className,\n ...props\n}: CalendarWidgetProps) {\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 const today = new Date();\n const [currentMonth, setCurrentMonth] = useState(today.getMonth());\n const [currentYear, setCurrentYear] = useState(today.getFullYear());\n const [isCollapsed, setIsCollapsed] = useState(false);\n const [selectedDate, setSelectedDate] = useState<Date | null>(today);\n\n const { data: events = [], isLoading, isError } = useCalendarEvents();\n\n // Generate calendar grids\n const monthGrid = useMemo(\n () => generateMonthGrid(currentYear, currentMonth),\n [currentYear, currentMonth],\n );\n const weekGrid = useMemo(() => generateWeekGrid(), []);\n\n // Get events for selected date\n const selectedDateEvents = useMemo(() => {\n if (!selectedDate) return [];\n return getEventsForDate(selectedDate, events);\n }, [selectedDate, events]);\n\n // Navigation handlers\n const goToPrevMonth = () => {\n if (currentMonth === 0) {\n setCurrentMonth(11);\n setCurrentYear(currentYear - 1);\n } else {\n setCurrentMonth(currentMonth - 1);\n }\n };\n\n const goToNextMonth = () => {\n if (currentMonth === 11) {\n setCurrentMonth(0);\n setCurrentYear(currentYear + 1);\n } else {\n setCurrentMonth(currentMonth + 1);\n }\n };\n\n const monthYearLabel = new Date(currentYear, currentMonth).toLocaleString(\n \"en-US\",\n {\n month: \"long\",\n year: \"numeric\",\n },\n );\n\n return (\n <div\n className={`@container overflow-hidden rounded-${borderRadius} bg-${backgroundColor} text-${textColor} ${className}`}\n style={{ backgroundImage }}\n {...props}\n >\n {/* Title */}\n {titleEnabled && titleText && (\n <div className={`p-${padding} pb-0`}>\n <h2 className={`text-${titleFontSize} font-bold text-${titleColor}`}>\n {titleText}\n </h2>\n </div>\n )}\n\n {/* Loading state */}\n {isLoading ? (\n <div\n className={`flex min-h-[300px] items-center justify-center p-${padding}`}\n >\n <div className=\"h-8 w-8 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n </div>\n ) : isError ? (\n /* Error state */\n <ErrorState />\n ) : (\n <div className={`p-${padding}`}>\n {/* Desktop: Side by side layout */}\n <div className=\"@md:flex @md:gap-6\">\n {/* Calendar Section */}\n <div className=\"@md:flex-none\">\n {/* Month/Year Header with Navigation */}\n <div className=\"mb-4 flex items-center justify-between\">\n <button\n onClick={goToPrevMonth}\n className=\"hover:bg-foreground/10 rounded-full p-1\"\n aria-label=\"Previous month\"\n >\n <FontAwesomeIcon icon={faChevronLeft} />\n </button>\n\n <span className=\"text-base font-semibold\">\n {monthYearLabel}\n </span>\n\n <button\n onClick={goToNextMonth}\n className=\"hover:bg-foreground/10 rounded-full p-1\"\n aria-label=\"Next month\"\n >\n <FontAwesomeIcon icon={faChevronRight} />\n </button>\n </div>\n\n {/* Day Headers */}\n <div className=\"mb-2 grid grid-cols-7 gap-1 text-center\">\n {dayNames.map((day, idx) => (\n <div\n key={idx}\n className=\"text-xs font-medium uppercase opacity-60\"\n >\n {day}\n </div>\n ))}\n </div>\n\n {/* Calendar Grid - Week view on mobile when collapsed */}\n {isCollapsed && (\n <div className=\"grid grid-cols-7 gap-1 @md:hidden\">\n {weekGrid.map(({ date, isToday }, idx) => (\n <DayButton\n key={idx}\n date={date}\n isToday={isToday}\n isSelected={\n selectedDate?.toDateString() === date.toDateString()\n }\n hasEvents={hasEventsOnDate(date, events)}\n accentColor={accentColor}\n onClick={() => setSelectedDate(date)}\n />\n ))}\n </div>\n )}\n\n {/* Month view (always on desktop, when not collapsed on mobile) */}\n <div\n className={`grid grid-cols-7 gap-1 ${isCollapsed ? \"hidden @md:grid\" : \"grid\"}`}\n >\n {monthGrid.map((day, idx) => {\n if (!day.date) {\n return <div key={idx} className=\"aspect-square\" />;\n }\n\n return (\n <DayButton\n key={idx}\n date={day.date}\n isToday={day.isToday}\n isSelected={\n selectedDate?.toDateString() === day.date.toDateString()\n }\n hasEvents={hasEventsOnDate(day.date, events)}\n accentColor={accentColor}\n onClick={() => setSelectedDate(day.date)}\n extraClassName=\"p-2 @md:m-3\"\n />\n );\n })}\n </div>\n\n {/* Mobile collapse toggle */}\n <button\n onClick={() => setIsCollapsed(!isCollapsed)}\n className={`ml-2 flex w-full items-center justify-center gap-3 rounded-full p-1 text-${textColor}/50 @md:hidden`}\n aria-label={\n isCollapsed ? \"Expand calendar\" : \"Collapse calendar\"\n }\n >\n <div className={`h-1 w-1/2 bg-${textColor}/50`} />\n {isCollapsed ? (\n <FontAwesomeIcon icon={faChevronsDown} />\n ) : (\n <FontAwesomeIcon icon={faChevronsUp} />\n )}\n <div className={`h-1 w-1/2 bg-${textColor}/50`} />\n </button>\n </div>\n\n {/* Events Panel */}\n <div className=\"border-muted mt-4 pt-4 @md:mt-0 @md:w-56 @md:border-l @md:pt-0 @md:pl-6\">\n <div className=\"mb-2 text-sm font-medium opacity-60\">\n {selectedDate\n ? selectedDate.toLocaleDateString(\"en-US\", {\n weekday: \"long\",\n month: \"short\",\n day: \"numeric\",\n })\n : \"Select a date\"}\n </div>\n\n {selectedDateEvents.length > 0 ? (\n <div className=\"flex flex-col gap-3\">\n {selectedDateEvents.map((event, idx) => (\n <EventItem\n key={`${event.id}-${idx}`}\n {...event}\n accentColor={`var(--${accentColor})`}\n />\n ))}\n </div>\n ) : (\n <div className=\"text-sm opacity-50\">No events</div>\n )}\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport const calendarWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"CalendarWidget\",\n displayName: \"Calendar Widget\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [\n // Styling Tab - Title Group\n {\n key: \"titleEnabled\",\n label: \"Widget Title\",\n type: \"boolean\",\n description: \"Enable the title displayed above the calendar\",\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 calendar\",\n defaultValue: \"Calendar\",\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 // Styling Tab - Design Group\n {\n type: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Background for the calendar 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 calendar content\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getColorField({\n key: \"accentColor\",\n label: \"Accent Color\",\n description: \"Color used for today highlight and event indicators\",\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 calendar 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 calendar container\",\n defaultValue: \"md\",\n tab: \"styling\",\n group: \"Design\",\n }),\n ],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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I;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;uDACgB;GACf,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;oDACY;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EAGF;GACE,MAAM;GACN,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;oDACa;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;oDACY;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF;GACE,KAAK;GACL,MAAM;GACN,OAAO;GACP,KAAK;GACL,OAAO;GACR;sDACe;GACd,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;2DACmB;GACnB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACH;CACF"}
|
|
@@ -63,7 +63,7 @@ function useActivities() {
|
|
|
63
63
|
isPreview ? "preview" : baseUrl
|
|
64
64
|
],
|
|
65
65
|
queryFn: async ({ signal }) => {
|
|
66
|
-
const url = baseUrl ? `${baseUrl}/
|
|
66
|
+
const url = baseUrl ? `${baseUrl}/v1.1/activities.json` : "/v1.1/activities.json";
|
|
67
67
|
const response = await fetch(url, {
|
|
68
68
|
headers: {
|
|
69
69
|
"content-type": "application/json",
|
|
@@ -388,4 +388,4 @@ const recentActivityWidgetPropertySchema = {
|
|
|
388
388
|
//#endregion
|
|
389
389
|
export { RecentActivityWidget_exports as n, recentActivityWidgetPropertySchema as r, RecentActivityWidget as t };
|
|
390
390
|
|
|
391
|
-
//# sourceMappingURL=RecentActivityWidget-
|
|
391
|
+
//# sourceMappingURL=RecentActivityWidget-CxEgRz3c.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RecentActivityWidget-CxEgRz3c.mjs","names":[],"sources":["../src/hooks/use-activities.preview.ts","../src/hooks/use-activities.ts","../src/widgets/RecentActivityWidget.tsx"],"sourcesContent":["import type { Activity } from \"./use-activities.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 } from \"@tanstack/react-query\";\nimport { useDataSourceConfig } from \"@fluid-app/rep-core/data-sources/context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/rep-core/data-sources/preview-context\";\nimport { PREVIEW_DATA } from \"./use-activities.preview\";\nimport type {\n Activity,\n ActivityContact,\n ActivityVisitor,\n ApiActivity,\n} from \"./use-activities.types\";\n\nexport type {\n ActivityContact,\n ActivityVisitor,\n ActivitySlug,\n ApiActivity,\n Activity,\n} from \"./use-activities.types\";\n\ntype PaginationInfo = {\n current: number;\n previous: number | null;\n next: number | null;\n per_page: number;\n pages: number;\n count: number;\n};\n\ntype ApiResponse = [{ pagination: PaginationInfo }, { items: ApiActivity[] }];\n\nexport function useActivities() {\n const { baseUrl, getApiHeaders } = useDataSourceConfig();\n const { isPreview } = useWidgetPreviewContext();\n\n return useQuery({\n queryKey: [\n \"rep-widget-use\",\n \"activities\",\n isPreview ? \"preview\" : baseUrl,\n ] as const,\n queryFn: async ({ signal }): Promise<Activity[]> => {\n const url = baseUrl\n ? `${baseUrl}/v1.1/activities.json`\n : \"/v1.1/activities.json\";\n const response = await fetch(url, {\n headers: {\n \"content-type\": \"application/json\",\n ...getApiHeaders?.(),\n },\n signal,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch activities: ${response.status}`);\n }\n\n const data: ApiResponse = await response.json();\n return transformActivities(data);\n },\n enabled: !isPreview,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\n });\n}\n\n// Transform and format helpers\nfunction getUserName(\n contact: ActivityContact | null,\n visitor: ActivityVisitor | null,\n): string {\n if (contact?.full_name) {\n return contact.full_name;\n }\n if (contact?.first_name && contact?.last_name) {\n return `${contact.first_name} ${contact.last_name}`;\n }\n if (visitor?.city && visitor?.state) {\n return `Visitor from ${visitor.city}, ${visitor.state}`;\n }\n return \"Unknown Visitor\";\n}\n\nfunction transformActivities(rawData: ApiResponse): Activity[] {\n const itemsObj = rawData[1];\n if (!itemsObj?.items) return [];\n\n return itemsObj.items\n .map((activity) => ({\n id: activity.id,\n userName: getUserName(activity.contact, activity.visitor),\n avatarUrl: activity.contact?.avatar_url ?? null,\n activityType: formatActivityType(activity.slug),\n targetName: activity.description || activity.title,\n timestamp: activity.created_at,\n slug: activity.slug,\n }))\n .sort(\n (a, b) =>\n new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),\n );\n}\n\nfunction formatActivityType(slug: string): string {\n return slug\n .split(\"_\")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \");\n}\n","import { useMemo, type ComponentProps } from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n FontSizeOptions,\n PaddingOptions,\n} from \"@fluid-app/rep-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/rep-core/registries\";\nimport {\n getBorderRadiusField,\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 { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport type { IconDefinition } from \"@fortawesome/fontawesome-svg-core\";\nimport {\n faBell,\n faCalendar,\n faSquareCheck,\n faEye,\n faComment,\n faPlay,\n faCartShopping,\n faStar,\n faTrophy,\n faUser,\n faUserPlus,\n} from \"@fortawesome/pro-regular-svg-icons\";\n\n// Format timestamp to time string\nconst formatTimestamp = (timestamp: string): string => {\n const date = new Date(timestamp);\n return date.toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true,\n });\n};\n\n// Format date for grouping header\nconst formatDateHeader = (timestamp: string): string => {\n const date = new Date(timestamp);\n return date.toLocaleDateString(\"en-US\", {\n month: \"long\",\n day: \"numeric\",\n });\n};\n\n// Get date string for grouping (without time)\nconst getDateKey = (timestamp: string): string => {\n const date = new Date(timestamp);\n return date.toISOString().split(\"T\")[0] ?? timestamp;\n};\n\n// Group activities by date\nconst groupActivitiesByDate = (\n activities: Activity[],\n): Map<string, Activity[]> => {\n const grouped = new Map<string, Activity[]>();\n\n for (const activity of activities) {\n const dateKey = getDateKey(activity.timestamp);\n const existing = grouped.get(dateKey);\n if (existing) {\n existing.push(activity);\n } else {\n grouped.set(dateKey, [activity]);\n }\n }\n\n return grouped;\n};\n\n// Activity slug to icon mapping\nconst ACTIVITY_ICON_MAP: Record<ActivitySlug, IconDefinition> = {\n // Orders/Cart\n order_placed: faCartShopping,\n abandoned_cart: faCartShopping,\n cart_items_added: faCartShopping,\n new_cart_items_added: faCartShopping,\n // Messages\n direct_message: faComment,\n comment_reply: faComment,\n message_received: faComment,\n message_sent: faComment,\n // Video\n video: faPlay,\n video_complete: faPlay,\n video_contact: faPlay,\n video_complete_contact: faPlay,\n // Leads\n new_lead: faUserPlus,\n page_views_contact: faUserPlus,\n smart_link_clicked: faUserPlus,\n // Page Views\n page_views: faEye,\n // Events\n upcoming_event: faCalendar,\n // Reviews\n review_left: faStar,\n // Tasks\n tasks: faSquareCheck,\n // Announcements\n announcements: faBell,\n // Fantasy\n fantasy_point: faTrophy,\n};\n\nconst getActivityIcon = (slug: ActivitySlug) =>\n ACTIVITY_ICON_MAP[slug] ?? faUser;\n\n// Activity feed item component\ntype ActivityFeedItemProps = {\n activity: Activity;\n accentColor: ColorOptions;\n textColor: ColorOptions;\n};\n\nfunction ActivityFeedItem({\n activity,\n accentColor,\n textColor,\n}: ActivityFeedItemProps) {\n const icon = getActivityIcon(activity.slug);\n\n return (\n <div className=\"flex w-full items-start gap-1.5\">\n {/* Avatar */}\n <div className=\"shrink-0\">\n {activity.avatarUrl ? (\n <div className=\"relative size-8 overflow-hidden rounded-full\">\n <img\n src={activity.avatarUrl}\n alt={activity.userName}\n className=\"size-full object-cover\"\n />\n <div className=\"border-foreground/[0.08] absolute inset-0 rounded-full border\" />\n </div>\n ) : (\n <div\n className={`bg-muted flex size-8 items-center justify-center rounded-full`}\n >\n <FontAwesomeIcon\n icon={icon}\n className={`size-3.5 text-${textColor} opacity-60`}\n />\n </div>\n )}\n </div>\n\n {/* Content */}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex w-full items-center gap-1.5\">\n <p\n className={`flex-1 text-base font-semibold text-${textColor} truncate`}\n >\n {activity.activityType}\n </p>\n <p className={`text-xs text-${textColor} shrink-0 opacity-50`}>\n {formatTimestamp(activity.timestamp)}\n </p>\n </div>\n <p className={`text-sm text-${textColor} opacity-80`}>\n <span className={`font-medium text-${accentColor}`}>\n {activity.userName}\n </span>{\" \"}\n {activity.targetName}\n </p>\n </div>\n </div>\n );\n}\n\ntype RecentActivityWidgetProps = ComponentProps<\"div\"> & {\n // Title\n titleEnabled?: boolean;\n titleText?: string;\n titleFontSize?: FontSizeOptions;\n titleColor?: ColorOptions;\n\n // Styling\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n\n // Activity settings\n maxItemsToShow?: number;\n};\n\nexport function RecentActivityWidget({\n // Title defaults\n titleEnabled = true,\n titleText = \"Recent Activity\",\n titleFontSize = \"lg\",\n titleColor = \"foreground\",\n\n // Styling defaults\n background = {\n type: \"solid\",\n color: \"background\",\n },\n textColor = \"foreground\",\n accentColor = \"primary\",\n padding = 4,\n borderRadius = \"md\",\n\n // Activity defaults\n maxItemsToShow = 5,\n\n className,\n ...props\n}: RecentActivityWidgetProps) {\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 const { data: activities = [], isLoading, isError } = useActivities();\n\n const groupedActivities = useMemo(\n () => groupActivitiesByDate(activities),\n [activities],\n );\n\n const totalCount = Math.min(activities.length, maxItemsToShow);\n\n // Get activities to display (limited by maxItemsToShow)\n const activitiesToShow = useMemo(() => {\n const result: { date: string; items: Activity[] }[] = [];\n let count = 0;\n\n for (const [dateKey, items] of groupedActivities) {\n if (count >= maxItemsToShow) break;\n\n const remainingSlots = maxItemsToShow - count;\n const itemsToAdd = items.slice(0, remainingSlots);\n\n if (itemsToAdd.length > 0) {\n result.push({\n date: formatDateHeader(items[0]?.timestamp ?? dateKey),\n items: itemsToAdd,\n });\n count += itemsToAdd.length;\n }\n }\n\n return result;\n }, [groupedActivities, maxItemsToShow]);\n\n return (\n <div\n className={`@container overflow-hidden rounded-${borderRadius} bg-${backgroundColor} text-${textColor} border-muted border ${className}`}\n style={{ backgroundImage }}\n {...props}\n >\n <div className={`p-${padding} flex flex-col gap-2`}>\n {/* Header */}\n {titleEnabled && titleText && (\n <div className=\"flex w-full items-start gap-2\">\n <h2\n className={`flex-1 text-${titleFontSize} font-bold text-${titleColor}`}\n >\n {titleText}\n </h2>\n {!isLoading && (\n <span\n className={`text-4xl font-bold text-${textColor} leading-none`}\n >\n {totalCount.toString().padStart(2, \"0\")}\n </span>\n )}\n </div>\n )}\n\n {/* Loading state */}\n {isLoading ? (\n <div className=\"flex min-h-[200px] items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n </div>\n ) : isError ? (\n /* Error state */\n <ErrorState />\n ) : activities.length === 0 ? (\n /* Empty state */\n <div className=\"flex min-h-[200px] flex-col items-center justify-center gap-2\">\n <FontAwesomeIcon\n icon={faUser}\n className={`size-12 text-${textColor} opacity-30`}\n />\n <p\n className={`text-base font-semibold text-${textColor} opacity-50`}\n >\n No Activity To Report\n </p>\n <p className={`text-sm text-${textColor} opacity-40`}>\n You'll Do Great!\n </p>\n </div>\n ) : (\n /* Activity list */\n <div className=\"flex flex-col gap-4\">\n {activitiesToShow.map((group, groupIndex) => (\n <div key={groupIndex} className=\"flex flex-col gap-4\">\n {/* Date header */}\n <p className={`text-base font-semibold text-${textColor}`}>\n {group.date}\n </p>\n\n {/* Activity items */}\n <div className=\"flex flex-col gap-4\">\n {group.items.map((activity) => (\n <ActivityFeedItem\n key={activity.id}\n activity={activity}\n accentColor={accentColor}\n textColor={textColor}\n />\n ))}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n\nexport const recentActivityWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"RecentActivityWidget\",\n displayName: \"Recent Activity Widget\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [\n // Styling Tab - Title Group\n {\n key: \"titleEnabled\",\n label: \"Widget Title\",\n type: \"boolean\",\n description: \"Enable the title displayed above the activity 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: \"Recent 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 // Styling Tab - Design Group\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: \"Color used for links and highlights\",\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\n // Styling Tab - Display Group\n {\n key: \"maxItemsToShow\",\n label: \"Max Items\",\n type: \"number\",\n description: \"Maximum number of activity items to display\",\n defaultValue: 5,\n tab: \"styling\",\n group: \"Display\",\n },\n ],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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"}
|
|
@@ -63,7 +63,7 @@ function useActivities() {
|
|
|
63
63
|
isPreview ? "preview" : baseUrl
|
|
64
64
|
],
|
|
65
65
|
queryFn: async ({ signal }) => {
|
|
66
|
-
const url = baseUrl ? `${baseUrl}/
|
|
66
|
+
const url = baseUrl ? `${baseUrl}/v1.1/activities.json` : "/v1.1/activities.json";
|
|
67
67
|
const response = await fetch(url, {
|
|
68
68
|
headers: {
|
|
69
69
|
"content-type": "application/json",
|
|
@@ -405,4 +405,4 @@ Object.defineProperty(exports, "recentActivityWidgetPropertySchema", {
|
|
|
405
405
|
}
|
|
406
406
|
});
|
|
407
407
|
|
|
408
|
-
//# sourceMappingURL=RecentActivityWidget-
|
|
408
|
+
//# sourceMappingURL=RecentActivityWidget-u1zCrwGF.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RecentActivityWidget-u1zCrwGF.cjs","names":["faCartShopping","faComment","faPlay","faUserPlus","faEye","faCalendar","faStar","faSquareCheck","faBell","faTrophy","faUser","FontAwesomeIcon","ErrorState"],"sources":["../src/hooks/use-activities.preview.ts","../src/hooks/use-activities.ts","../src/widgets/RecentActivityWidget.tsx"],"sourcesContent":["import type { Activity } from \"./use-activities.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 } from \"@tanstack/react-query\";\nimport { useDataSourceConfig } from \"@fluid-app/rep-core/data-sources/context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/rep-core/data-sources/preview-context\";\nimport { PREVIEW_DATA } from \"./use-activities.preview\";\nimport type {\n Activity,\n ActivityContact,\n ActivityVisitor,\n ApiActivity,\n} from \"./use-activities.types\";\n\nexport type {\n ActivityContact,\n ActivityVisitor,\n ActivitySlug,\n ApiActivity,\n Activity,\n} from \"./use-activities.types\";\n\ntype PaginationInfo = {\n current: number;\n previous: number | null;\n next: number | null;\n per_page: number;\n pages: number;\n count: number;\n};\n\ntype ApiResponse = [{ pagination: PaginationInfo }, { items: ApiActivity[] }];\n\nexport function useActivities() {\n const { baseUrl, getApiHeaders } = useDataSourceConfig();\n const { isPreview } = useWidgetPreviewContext();\n\n return useQuery({\n queryKey: [\n \"rep-widget-use\",\n \"activities\",\n isPreview ? \"preview\" : baseUrl,\n ] as const,\n queryFn: async ({ signal }): Promise<Activity[]> => {\n const url = baseUrl\n ? `${baseUrl}/v1.1/activities.json`\n : \"/v1.1/activities.json\";\n const response = await fetch(url, {\n headers: {\n \"content-type\": \"application/json\",\n ...getApiHeaders?.(),\n },\n signal,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch activities: ${response.status}`);\n }\n\n const data: ApiResponse = await response.json();\n return transformActivities(data);\n },\n enabled: !isPreview,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\n });\n}\n\n// Transform and format helpers\nfunction getUserName(\n contact: ActivityContact | null,\n visitor: ActivityVisitor | null,\n): string {\n if (contact?.full_name) {\n return contact.full_name;\n }\n if (contact?.first_name && contact?.last_name) {\n return `${contact.first_name} ${contact.last_name}`;\n }\n if (visitor?.city && visitor?.state) {\n return `Visitor from ${visitor.city}, ${visitor.state}`;\n }\n return \"Unknown Visitor\";\n}\n\nfunction transformActivities(rawData: ApiResponse): Activity[] {\n const itemsObj = rawData[1];\n if (!itemsObj?.items) return [];\n\n return itemsObj.items\n .map((activity) => ({\n id: activity.id,\n userName: getUserName(activity.contact, activity.visitor),\n avatarUrl: activity.contact?.avatar_url ?? null,\n activityType: formatActivityType(activity.slug),\n targetName: activity.description || activity.title,\n timestamp: activity.created_at,\n slug: activity.slug,\n }))\n .sort(\n (a, b) =>\n new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),\n );\n}\n\nfunction formatActivityType(slug: string): string {\n return slug\n .split(\"_\")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \");\n}\n","import { useMemo, type ComponentProps } from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n FontSizeOptions,\n PaddingOptions,\n} from \"@fluid-app/rep-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/rep-core/registries\";\nimport {\n getBorderRadiusField,\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 { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport type { IconDefinition } from \"@fortawesome/fontawesome-svg-core\";\nimport {\n faBell,\n faCalendar,\n faSquareCheck,\n faEye,\n faComment,\n faPlay,\n faCartShopping,\n faStar,\n faTrophy,\n faUser,\n faUserPlus,\n} from \"@fortawesome/pro-regular-svg-icons\";\n\n// Format timestamp to time string\nconst formatTimestamp = (timestamp: string): string => {\n const date = new Date(timestamp);\n return date.toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true,\n });\n};\n\n// Format date for grouping header\nconst formatDateHeader = (timestamp: string): string => {\n const date = new Date(timestamp);\n return date.toLocaleDateString(\"en-US\", {\n month: \"long\",\n day: \"numeric\",\n });\n};\n\n// Get date string for grouping (without time)\nconst getDateKey = (timestamp: string): string => {\n const date = new Date(timestamp);\n return date.toISOString().split(\"T\")[0] ?? timestamp;\n};\n\n// Group activities by date\nconst groupActivitiesByDate = (\n activities: Activity[],\n): Map<string, Activity[]> => {\n const grouped = new Map<string, Activity[]>();\n\n for (const activity of activities) {\n const dateKey = getDateKey(activity.timestamp);\n const existing = grouped.get(dateKey);\n if (existing) {\n existing.push(activity);\n } else {\n grouped.set(dateKey, [activity]);\n }\n }\n\n return grouped;\n};\n\n// Activity slug to icon mapping\nconst ACTIVITY_ICON_MAP: Record<ActivitySlug, IconDefinition> = {\n // Orders/Cart\n order_placed: faCartShopping,\n abandoned_cart: faCartShopping,\n cart_items_added: faCartShopping,\n new_cart_items_added: faCartShopping,\n // Messages\n direct_message: faComment,\n comment_reply: faComment,\n message_received: faComment,\n message_sent: faComment,\n // Video\n video: faPlay,\n video_complete: faPlay,\n video_contact: faPlay,\n video_complete_contact: faPlay,\n // Leads\n new_lead: faUserPlus,\n page_views_contact: faUserPlus,\n smart_link_clicked: faUserPlus,\n // Page Views\n page_views: faEye,\n // Events\n upcoming_event: faCalendar,\n // Reviews\n review_left: faStar,\n // Tasks\n tasks: faSquareCheck,\n // Announcements\n announcements: faBell,\n // Fantasy\n fantasy_point: faTrophy,\n};\n\nconst getActivityIcon = (slug: ActivitySlug) =>\n ACTIVITY_ICON_MAP[slug] ?? faUser;\n\n// Activity feed item component\ntype ActivityFeedItemProps = {\n activity: Activity;\n accentColor: ColorOptions;\n textColor: ColorOptions;\n};\n\nfunction ActivityFeedItem({\n activity,\n accentColor,\n textColor,\n}: ActivityFeedItemProps) {\n const icon = getActivityIcon(activity.slug);\n\n return (\n <div className=\"flex w-full items-start gap-1.5\">\n {/* Avatar */}\n <div className=\"shrink-0\">\n {activity.avatarUrl ? (\n <div className=\"relative size-8 overflow-hidden rounded-full\">\n <img\n src={activity.avatarUrl}\n alt={activity.userName}\n className=\"size-full object-cover\"\n />\n <div className=\"border-foreground/[0.08] absolute inset-0 rounded-full border\" />\n </div>\n ) : (\n <div\n className={`bg-muted flex size-8 items-center justify-center rounded-full`}\n >\n <FontAwesomeIcon\n icon={icon}\n className={`size-3.5 text-${textColor} opacity-60`}\n />\n </div>\n )}\n </div>\n\n {/* Content */}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex w-full items-center gap-1.5\">\n <p\n className={`flex-1 text-base font-semibold text-${textColor} truncate`}\n >\n {activity.activityType}\n </p>\n <p className={`text-xs text-${textColor} shrink-0 opacity-50`}>\n {formatTimestamp(activity.timestamp)}\n </p>\n </div>\n <p className={`text-sm text-${textColor} opacity-80`}>\n <span className={`font-medium text-${accentColor}`}>\n {activity.userName}\n </span>{\" \"}\n {activity.targetName}\n </p>\n </div>\n </div>\n );\n}\n\ntype RecentActivityWidgetProps = ComponentProps<\"div\"> & {\n // Title\n titleEnabled?: boolean;\n titleText?: string;\n titleFontSize?: FontSizeOptions;\n titleColor?: ColorOptions;\n\n // Styling\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n\n // Activity settings\n maxItemsToShow?: number;\n};\n\nexport function RecentActivityWidget({\n // Title defaults\n titleEnabled = true,\n titleText = \"Recent Activity\",\n titleFontSize = \"lg\",\n titleColor = \"foreground\",\n\n // Styling defaults\n background = {\n type: \"solid\",\n color: \"background\",\n },\n textColor = \"foreground\",\n accentColor = \"primary\",\n padding = 4,\n borderRadius = \"md\",\n\n // Activity defaults\n maxItemsToShow = 5,\n\n className,\n ...props\n}: RecentActivityWidgetProps) {\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 const { data: activities = [], isLoading, isError } = useActivities();\n\n const groupedActivities = useMemo(\n () => groupActivitiesByDate(activities),\n [activities],\n );\n\n const totalCount = Math.min(activities.length, maxItemsToShow);\n\n // Get activities to display (limited by maxItemsToShow)\n const activitiesToShow = useMemo(() => {\n const result: { date: string; items: Activity[] }[] = [];\n let count = 0;\n\n for (const [dateKey, items] of groupedActivities) {\n if (count >= maxItemsToShow) break;\n\n const remainingSlots = maxItemsToShow - count;\n const itemsToAdd = items.slice(0, remainingSlots);\n\n if (itemsToAdd.length > 0) {\n result.push({\n date: formatDateHeader(items[0]?.timestamp ?? dateKey),\n items: itemsToAdd,\n });\n count += itemsToAdd.length;\n }\n }\n\n return result;\n }, [groupedActivities, maxItemsToShow]);\n\n return (\n <div\n className={`@container overflow-hidden rounded-${borderRadius} bg-${backgroundColor} text-${textColor} border-muted border ${className}`}\n style={{ backgroundImage }}\n {...props}\n >\n <div className={`p-${padding} flex flex-col gap-2`}>\n {/* Header */}\n {titleEnabled && titleText && (\n <div className=\"flex w-full items-start gap-2\">\n <h2\n className={`flex-1 text-${titleFontSize} font-bold text-${titleColor}`}\n >\n {titleText}\n </h2>\n {!isLoading && (\n <span\n className={`text-4xl font-bold text-${textColor} leading-none`}\n >\n {totalCount.toString().padStart(2, \"0\")}\n </span>\n )}\n </div>\n )}\n\n {/* Loading state */}\n {isLoading ? (\n <div className=\"flex min-h-[200px] items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n </div>\n ) : isError ? (\n /* Error state */\n <ErrorState />\n ) : activities.length === 0 ? (\n /* Empty state */\n <div className=\"flex min-h-[200px] flex-col items-center justify-center gap-2\">\n <FontAwesomeIcon\n icon={faUser}\n className={`size-12 text-${textColor} opacity-30`}\n />\n <p\n className={`text-base font-semibold text-${textColor} opacity-50`}\n >\n No Activity To Report\n </p>\n <p className={`text-sm text-${textColor} opacity-40`}>\n You'll Do Great!\n </p>\n </div>\n ) : (\n /* Activity list */\n <div className=\"flex flex-col gap-4\">\n {activitiesToShow.map((group, groupIndex) => (\n <div key={groupIndex} className=\"flex flex-col gap-4\">\n {/* Date header */}\n <p className={`text-base font-semibold text-${textColor}`}>\n {group.date}\n </p>\n\n {/* Activity items */}\n <div className=\"flex flex-col gap-4\">\n {group.items.map((activity) => (\n <ActivityFeedItem\n key={activity.id}\n activity={activity}\n accentColor={accentColor}\n textColor={textColor}\n />\n ))}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n\nexport const recentActivityWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"RecentActivityWidget\",\n displayName: \"Recent Activity Widget\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [\n // Styling Tab - Title Group\n {\n key: \"titleEnabled\",\n label: \"Widget Title\",\n type: \"boolean\",\n description: \"Enable the title displayed above the activity 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: \"Recent 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 // Styling Tab - Design Group\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: \"Color used for links and highlights\",\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\n // Styling Tab - Display Group\n {\n key: \"maxItemsToShow\",\n label: \"Max Items\",\n type: \"number\",\n description: \"Maximum number of activity items to display\",\n defaultValue: 5,\n tab: \"styling\",\n group: \"Display\",\n },\n ],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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P,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;oDACY;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EAGF;GACE,MAAM;GACN,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;oDACa;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;oDACY;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF;GACE,KAAK;GACL,MAAM;GACN,OAAO;GACP,KAAK;GACL,OAAO;GACR;sDACe;GACd,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;2DACmB;GACnB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EAGF;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACF;CACF"}
|
package/dist/widgets/index.cjs
CHANGED
|
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
require("../chunk-CZWwpsFl.cjs");
|
|
3
3
|
const require_TextWidget = require("../TextWidget-Cn7-yZGk.cjs");
|
|
4
4
|
const require_AlertWidget = require("../AlertWidget-DU_HP0EW.cjs");
|
|
5
|
-
const require_CalendarWidget = require("../CalendarWidget-
|
|
5
|
+
const require_CalendarWidget = require("../CalendarWidget-s7KgQ5JD.cjs");
|
|
6
6
|
const require_CarouselWidget = require("../CarouselWidget-DLADgLPJ.cjs");
|
|
7
7
|
const require_CatchUpWidget = require("../CatchUpWidget-DiqdwPvE.cjs");
|
|
8
8
|
const require_ChartWidget = require("../ChartWidget-DhAVW8w4.cjs");
|
|
@@ -14,7 +14,7 @@ const require_ListWidget = require("../ListWidget-CxaLm0D5.cjs");
|
|
|
14
14
|
const require_MySiteWidget = require("../MySiteWidget-iRPl6b2F.cjs");
|
|
15
15
|
const require_NestedWidget = require("../NestedWidget-DavPMVii.cjs");
|
|
16
16
|
const require_QuickShareWidget = require("../QuickShareWidget-BIOkquPG.cjs");
|
|
17
|
-
const require_RecentActivityWidget = require("../RecentActivityWidget-
|
|
17
|
+
const require_RecentActivityWidget = require("../RecentActivityWidget-u1zCrwGF.cjs");
|
|
18
18
|
const require_SpacerWidget = require("../SpacerWidget-D-Foe9wG.cjs");
|
|
19
19
|
const require_TableWidget = require("../TableWidget-BeG92tkU.cjs");
|
|
20
20
|
const require_ToDoWidget = require("../ToDoWidget-CK87BavM.cjs");
|
|
@@ -23,7 +23,7 @@ let _fluid_app_rep_core_registries = require("@fluid-app/rep-core/registries");
|
|
|
23
23
|
//#region src/widgets/index.ts
|
|
24
24
|
const widgetPropertySchemas = {
|
|
25
25
|
AlertWidget: () => Promise.resolve().then(() => require("../AlertWidget-DU_HP0EW.cjs")).then((n) => n.AlertWidget_exports).then((m) => m.alertWidgetPropertySchema),
|
|
26
|
-
CalendarWidget: () => Promise.resolve().then(() => require("../CalendarWidget-
|
|
26
|
+
CalendarWidget: () => Promise.resolve().then(() => require("../CalendarWidget-s7KgQ5JD.cjs")).then((n) => n.CalendarWidget_exports).then((m) => m.calendarWidgetPropertySchema),
|
|
27
27
|
CarouselWidget: () => Promise.resolve().then(() => require("../CarouselWidget-DLADgLPJ.cjs")).then((n) => n.CarouselWidget_exports).then((m) => m.carouselWidgetPropertySchema),
|
|
28
28
|
CatchUpWidget: () => Promise.resolve().then(() => require("../CatchUpWidget-DiqdwPvE.cjs")).then((n) => n.CatchUpWidget_exports).then((m) => m.catchUpWidgetPropertySchema),
|
|
29
29
|
ChartWidget: () => Promise.resolve().then(() => require("../ChartWidget-DhAVW8w4.cjs")).then((n) => n.ChartWidget_exports).then((m) => m.chartWidgetPropertySchema),
|
|
@@ -35,7 +35,7 @@ const widgetPropertySchemas = {
|
|
|
35
35
|
MySiteWidget: () => Promise.resolve().then(() => require("../MySiteWidget-iRPl6b2F.cjs")).then((n) => n.MySiteWidget_exports).then((m) => m.mySiteWidgetPropertySchema),
|
|
36
36
|
NestedWidget: () => Promise.resolve().then(() => require("../NestedWidget-DavPMVii.cjs")).then((n) => n.NestedWidget_exports).then((m) => m.nestedWidgetPropertySchema),
|
|
37
37
|
QuickShareWidget: () => Promise.resolve().then(() => require("../QuickShareWidget-BIOkquPG.cjs")).then((n) => n.QuickShareWidget_exports).then((m) => m.quickShareWidgetPropertySchema),
|
|
38
|
-
RecentActivityWidget: () => Promise.resolve().then(() => require("../RecentActivityWidget-
|
|
38
|
+
RecentActivityWidget: () => Promise.resolve().then(() => require("../RecentActivityWidget-u1zCrwGF.cjs")).then((n) => n.RecentActivityWidget_exports).then((m) => m.recentActivityWidgetPropertySchema),
|
|
39
39
|
SpacerWidget: () => Promise.resolve().then(() => require("../SpacerWidget-D-Foe9wG.cjs")).then((n) => n.SpacerWidget_exports).then((m) => m.spacerWidgetPropertySchema),
|
|
40
40
|
TableWidget: () => Promise.resolve().then(() => require("../TableWidget-BeG92tkU.cjs")).then((n) => n.TableWidget_exports).then((m) => m.tableWidgetPropertySchema),
|
|
41
41
|
TextWidget: () => Promise.resolve().then(() => require("../TextWidget-Cn7-yZGk.cjs")).then((n) => n.TextWidget_exports).then((m) => m.textWidgetPropertySchema),
|
package/dist/widgets/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as gapValues } from "../fields-C29fgJje.mjs";
|
|
2
2
|
import { r as textWidgetPropertySchema, t as TextWidget } from "../TextWidget-CQxOWNZK.mjs";
|
|
3
3
|
import { r as alertWidgetPropertySchema, t as AlertWidget } from "../AlertWidget-CiMRJYmn.mjs";
|
|
4
|
-
import { r as calendarWidgetPropertySchema, t as CalendarWidget } from "../CalendarWidget-
|
|
4
|
+
import { r as calendarWidgetPropertySchema, t as CalendarWidget } from "../CalendarWidget-DshBk5tG.mjs";
|
|
5
5
|
import { r as carouselWidgetPropertySchema, t as CarouselWidget } from "../CarouselWidget-0RP-Rd1D.mjs";
|
|
6
6
|
import { r as catchUpWidgetPropertySchema, t as CatchUpWidget } from "../CatchUpWidget-CUnCumIf.mjs";
|
|
7
7
|
import { r as chartWidgetPropertySchema, t as ChartWidget } from "../ChartWidget-DrF1e1sy.mjs";
|
|
@@ -13,7 +13,7 @@ import { r as listWidgetPropertySchema, t as ListWidget } from "../ListWidget-EX
|
|
|
13
13
|
import { r as mySiteWidgetPropertySchema, t as MySiteWidget } from "../MySiteWidget-BTyb5AJM.mjs";
|
|
14
14
|
import { r as nestedWidgetPropertySchema, t as NestedWidget } from "../NestedWidget-CwyVsZ-f.mjs";
|
|
15
15
|
import { r as quickShareWidgetPropertySchema, t as QuickShareWidget } from "../QuickShareWidget-BUbuTQFQ.mjs";
|
|
16
|
-
import { r as recentActivityWidgetPropertySchema, t as RecentActivityWidget } from "../RecentActivityWidget-
|
|
16
|
+
import { r as recentActivityWidgetPropertySchema, t as RecentActivityWidget } from "../RecentActivityWidget-CxEgRz3c.mjs";
|
|
17
17
|
import { r as spacerWidgetPropertySchema, t as SpacerWidget } from "../SpacerWidget-Bs18KYH9.mjs";
|
|
18
18
|
import { r as tableWidgetPropertySchema, t as TableWidget } from "../TableWidget-Cc4kQaOE.mjs";
|
|
19
19
|
import { r as toDoWidgetPropertySchema, t as ToDoWidget } from "../ToDoWidget--KBpHqbA.mjs";
|
|
@@ -21,7 +21,7 @@ import { r as videoWidgetPropertySchema, t as VideoWidget } from "../VideoWidget
|
|
|
21
21
|
//#region src/widgets/index.ts
|
|
22
22
|
const widgetPropertySchemas = {
|
|
23
23
|
AlertWidget: () => import("../AlertWidget-CiMRJYmn.mjs").then((n) => n.n).then((m) => m.alertWidgetPropertySchema),
|
|
24
|
-
CalendarWidget: () => import("../CalendarWidget-
|
|
24
|
+
CalendarWidget: () => import("../CalendarWidget-DshBk5tG.mjs").then((n) => n.n).then((m) => m.calendarWidgetPropertySchema),
|
|
25
25
|
CarouselWidget: () => import("../CarouselWidget-0RP-Rd1D.mjs").then((n) => n.n).then((m) => m.carouselWidgetPropertySchema),
|
|
26
26
|
CatchUpWidget: () => import("../CatchUpWidget-CUnCumIf.mjs").then((n) => n.n).then((m) => m.catchUpWidgetPropertySchema),
|
|
27
27
|
ChartWidget: () => import("../ChartWidget-DrF1e1sy.mjs").then((n) => n.n).then((m) => m.chartWidgetPropertySchema),
|
|
@@ -33,7 +33,7 @@ const widgetPropertySchemas = {
|
|
|
33
33
|
MySiteWidget: () => import("../MySiteWidget-BTyb5AJM.mjs").then((n) => n.n).then((m) => m.mySiteWidgetPropertySchema),
|
|
34
34
|
NestedWidget: () => import("../NestedWidget-CwyVsZ-f.mjs").then((n) => n.n).then((m) => m.nestedWidgetPropertySchema),
|
|
35
35
|
QuickShareWidget: () => import("../QuickShareWidget-BUbuTQFQ.mjs").then((n) => n.n).then((m) => m.quickShareWidgetPropertySchema),
|
|
36
|
-
RecentActivityWidget: () => import("../RecentActivityWidget-
|
|
36
|
+
RecentActivityWidget: () => import("../RecentActivityWidget-CxEgRz3c.mjs").then((n) => n.n).then((m) => m.recentActivityWidgetPropertySchema),
|
|
37
37
|
SpacerWidget: () => import("../SpacerWidget-Bs18KYH9.mjs").then((n) => n.n).then((m) => m.spacerWidgetPropertySchema),
|
|
38
38
|
TableWidget: () => import("../TableWidget-Cc4kQaOE.mjs").then((n) => n.n).then((m) => m.tableWidgetPropertySchema),
|
|
39
39
|
TextWidget: () => import("../TextWidget-CQxOWNZK.mjs").then((n) => n.n).then((m) => m.textWidgetPropertySchema),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluid-app/rep-widgets",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"description": "Widget components for the Fluid rep platform",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -79,8 +79,8 @@
|
|
|
79
79
|
"tailwind-merge": "^3.0.2",
|
|
80
80
|
"tailwindcss": "^4.1.18",
|
|
81
81
|
"tw-animate-css": "^1.4.0",
|
|
82
|
-
"@fluid-app/
|
|
83
|
-
"@fluid-app/
|
|
82
|
+
"@fluid-app/ui-primitives": "0.1.6",
|
|
83
|
+
"@fluid-app/rep-core": "0.1.16"
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
86
|
"@tanstack/react-query": "^5.90.11",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CalendarWidget-BI8sv5HJ.cjs","names":["ErrorState","FontAwesomeIcon","faChevronLeft","faChevronRight","faChevronsDown","faChevronsUp"],"sources":["../src/hooks/use-calendar-events.preview.ts","../src/hooks/use-calendar-events.ts","../src/widgets/CalendarWidget.tsx"],"sourcesContent":["import type { CalendarEvent } from \"./use-calendar-events.types\";\n\nconst today = new Date();\nconst inTwoDays = new Date(today);\ninTwoDays.setDate(inTwoDays.getDate() + 2);\n\nfunction toISO(date: Date, hours: number, minutes = 0): string {\n const d = new Date(date);\n d.setHours(hours, minutes, 0, 0);\n return d.toISOString();\n}\n\nfunction toDateString(date: Date): string {\n return date.toISOString().split(\"T\")[0]!;\n}\n\nexport const PREVIEW_DATA: CalendarEvent[] = [\n {\n id: 1,\n title: \"Team Strategy Call\",\n start: toISO(today, 9, 0),\n end: toISO(today, 10, 0),\n status: \"active\",\n color: \"#4f46e5\",\n },\n {\n id: 2,\n title: \"Product Launch Webinar\",\n start: toISO(today, 14, 0),\n end: toISO(today, 15, 30),\n status: \"active\",\n color: \"#0891b2\",\n venue: \"Zoom\",\n },\n {\n id: 3,\n title: \"Annual Leadership Summit\",\n start: `${toDateString(inTwoDays)}T00:00:00.000Z`,\n end: `${toDateString(inTwoDays)}T23:59:59.000Z`,\n status: \"active\",\n color: \"#059669\",\n isAllDay: true,\n venue: \"Convention Center\",\n },\n];\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useDataSourceConfig } from \"@fluid-app/rep-core/data-sources/context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/rep-core/data-sources/preview-context\";\nimport { PREVIEW_DATA } from \"./use-calendar-events.preview\";\n\nimport type { CalendarEvent } from \"./use-calendar-events.types\";\n\nexport type { CalendarEvent } from \"./use-calendar-events.types\";\n\nexport function useCalendarEvents() {\n const { baseUrl, getApiHeaders } = useDataSourceConfig();\n const { isPreview } = useWidgetPreviewContext();\n\n return useQuery({\n queryKey: [\n \"rep-widget-use\",\n \"calendar-events\",\n isPreview ? \"preview\" : baseUrl,\n ] as const,\n queryFn: async ({ signal }): Promise<CalendarEvent[]> => {\n const url = baseUrl\n ? `${baseUrl}/company/events?status=active`\n : \"/company/events?status=active\";\n const response = await fetch(url, {\n headers: {\n \"content-type\": \"application/json\",\n ...getApiHeaders?.(),\n },\n signal,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch calendar events: ${response.status}`);\n }\n\n return response.json();\n },\n enabled: !isPreview,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\n });\n}\n","import { useState, useMemo, type ComponentProps } from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n FontSizeOptions,\n PaddingOptions,\n} from \"@fluid-app/rep-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/rep-core/registries\";\nimport {\n getBorderRadiusField,\n getColorField,\n getFontSizeField,\n getPaddingField,\n} from \"../core/fields\";\nimport {\n useCalendarEvents,\n type CalendarEvent,\n} from \"../hooks/use-calendar-events\";\nimport { ErrorState } from \"../components/error-state\";\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport {\n faChevronLeft,\n faChevronRight,\n faChevronsDown,\n faChevronsUp,\n} from \"@fortawesome/pro-regular-svg-icons\";\n\n// Utility to get days in a month\nconst getDaysInMonth = (year: number, month: number) => {\n return new Date(year, month + 1, 0).getDate();\n};\n\n// Utility to get the first day of month (0 = Sunday, 1 = Monday, etc.)\nconst getFirstDayOfMonth = (year: number, month: number) => {\n return new Date(year, month, 1).getDay();\n};\n\n// Generate calendar grid for a month\nconst generateMonthGrid = (year: number, month: number) => {\n const daysInMonth = getDaysInMonth(year, month);\n const firstDay = getFirstDayOfMonth(year, month);\n const today = new Date();\n\n const days: Array<{\n date: Date | null;\n isToday: boolean;\n isCurrentMonth: boolean;\n }> = [];\n\n // Add empty slots for days before the 1st\n for (let i = 0; i < firstDay; i++) {\n days.push({ date: null, isToday: false, isCurrentMonth: false });\n }\n\n // Add days of the month\n for (let day = 1; day <= daysInMonth; day++) {\n const date = new Date(year, month, day);\n const isToday =\n date.getDate() === today.getDate() &&\n date.getMonth() === today.getMonth() &&\n date.getFullYear() === today.getFullYear();\n days.push({ date, isToday, isCurrentMonth: true });\n }\n\n return days;\n};\n\n// Generate week grid for current week\nconst generateWeekGrid = () => {\n const today = new Date();\n const currentDay = today.getDay();\n const days: Array<{\n date: Date;\n isToday: boolean;\n }> = [];\n\n for (let i = 0; i < 7; i++) {\n const date = new Date(today);\n date.setDate(today.getDate() - currentDay + i);\n days.push({\n date,\n isToday: date.toDateString() === today.toDateString(),\n });\n }\n\n return days;\n};\n\n// Get events for a specific date\nconst getEventsForDate = (date: Date, events: CalendarEvent[]) => {\n const dayStart = new Date(date);\n dayStart.setHours(0, 0, 0, 0);\n const dayEnd = new Date(date);\n dayEnd.setHours(23, 59, 59, 999);\n\n return events.filter((event) => {\n const eventStart = new Date(event.start);\n const eventEnd = new Date(event.end);\n return eventStart <= dayEnd && eventEnd >= dayStart;\n });\n};\n\n// Check if a date has events\nconst hasEventsOnDate = (date: Date | null, events: CalendarEvent[]) => {\n if (!date) return false;\n return getEventsForDate(date, events).length > 0;\n};\n\n// Event item component\nconst EventItem = ({\n title,\n start,\n end,\n color,\n isAllDay,\n accentColor,\n}: CalendarEvent & { accentColor?: string }) => {\n const startTime = new Date(start).toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n const endTime = new Date(end).toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n\n return (\n <div className=\"flex gap-x-2\">\n <div\n className=\"w-0.5 shrink-0 rounded-full\"\n style={{ backgroundColor: color || accentColor }}\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-sm font-medium\">{title}</div>\n <div className=\"text-xs opacity-60\">\n {isAllDay ? \"All day\" : `${startTime} - ${endTime}`}\n </div>\n </div>\n </div>\n );\n};\n\nconst dayNames = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\n\ntype DayButtonProps = {\n date: Date;\n isToday: boolean;\n isSelected: boolean;\n hasEvents: boolean;\n accentColor: string;\n onClick: () => void;\n extraClassName?: string;\n};\n\nfunction DayButton({\n date,\n isToday,\n isSelected,\n hasEvents,\n accentColor,\n onClick,\n extraClassName = \"\",\n}: DayButtonProps) {\n return (\n <button\n onClick={onClick}\n className={`relative flex aspect-square items-center justify-center rounded-full text-sm transition-colors ${\n isSelected\n ? `bg-${accentColor} text-${accentColor}-foreground`\n : isToday\n ? `bg-${accentColor}/30 text-${accentColor}-foreground hover:bg-${accentColor}/50`\n : `hover:bg-${accentColor}/50 hover:text-${accentColor}-foreground`\n } ${extraClassName}`}\n >\n {date.getDate()}\n {hasEvents && (\n <div\n className={`absolute bottom-1 h-1 w-1 rounded-full bg-${accentColor}`}\n />\n )}\n </button>\n );\n}\n\ntype CalendarWidgetProps = ComponentProps<\"div\"> & {\n // Title\n titleEnabled?: boolean;\n titleText?: string;\n titleFontSize?: FontSizeOptions;\n titleColor?: ColorOptions;\n\n // Styling\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n};\n\nexport function CalendarWidget({\n // Title defaults\n titleEnabled = true,\n titleText = \"Calendar\",\n titleFontSize = \"xl\",\n titleColor = \"foreground\",\n\n // Styling defaults\n background = {\n type: \"solid\",\n color: \"background\",\n },\n textColor = \"foreground\",\n accentColor = \"primary\",\n padding = 4,\n borderRadius = \"md\",\n\n className,\n ...props\n}: CalendarWidgetProps) {\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 const today = new Date();\n const [currentMonth, setCurrentMonth] = useState(today.getMonth());\n const [currentYear, setCurrentYear] = useState(today.getFullYear());\n const [isCollapsed, setIsCollapsed] = useState(false);\n const [selectedDate, setSelectedDate] = useState<Date | null>(today);\n\n const { data: events = [], isLoading, isError } = useCalendarEvents();\n\n // Generate calendar grids\n const monthGrid = useMemo(\n () => generateMonthGrid(currentYear, currentMonth),\n [currentYear, currentMonth],\n );\n const weekGrid = useMemo(() => generateWeekGrid(), []);\n\n // Get events for selected date\n const selectedDateEvents = useMemo(() => {\n if (!selectedDate) return [];\n return getEventsForDate(selectedDate, events);\n }, [selectedDate, events]);\n\n // Navigation handlers\n const goToPrevMonth = () => {\n if (currentMonth === 0) {\n setCurrentMonth(11);\n setCurrentYear(currentYear - 1);\n } else {\n setCurrentMonth(currentMonth - 1);\n }\n };\n\n const goToNextMonth = () => {\n if (currentMonth === 11) {\n setCurrentMonth(0);\n setCurrentYear(currentYear + 1);\n } else {\n setCurrentMonth(currentMonth + 1);\n }\n };\n\n const monthYearLabel = new Date(currentYear, currentMonth).toLocaleString(\n \"en-US\",\n {\n month: \"long\",\n year: \"numeric\",\n },\n );\n\n return (\n <div\n className={`@container overflow-hidden rounded-${borderRadius} bg-${backgroundColor} text-${textColor} ${className}`}\n style={{ backgroundImage }}\n {...props}\n >\n {/* Title */}\n {titleEnabled && titleText && (\n <div className={`p-${padding} pb-0`}>\n <h2 className={`text-${titleFontSize} font-bold text-${titleColor}`}>\n {titleText}\n </h2>\n </div>\n )}\n\n {/* Loading state */}\n {isLoading ? (\n <div\n className={`flex min-h-[300px] items-center justify-center p-${padding}`}\n >\n <div className=\"h-8 w-8 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n </div>\n ) : isError ? (\n /* Error state */\n <ErrorState />\n ) : (\n <div className={`p-${padding}`}>\n {/* Desktop: Side by side layout */}\n <div className=\"@md:flex @md:gap-6\">\n {/* Calendar Section */}\n <div className=\"@md:flex-none\">\n {/* Month/Year Header with Navigation */}\n <div className=\"mb-4 flex items-center justify-between\">\n <button\n onClick={goToPrevMonth}\n className=\"hover:bg-foreground/10 rounded-full p-1\"\n aria-label=\"Previous month\"\n >\n <FontAwesomeIcon icon={faChevronLeft} />\n </button>\n\n <span className=\"text-base font-semibold\">\n {monthYearLabel}\n </span>\n\n <button\n onClick={goToNextMonth}\n className=\"hover:bg-foreground/10 rounded-full p-1\"\n aria-label=\"Next month\"\n >\n <FontAwesomeIcon icon={faChevronRight} />\n </button>\n </div>\n\n {/* Day Headers */}\n <div className=\"mb-2 grid grid-cols-7 gap-1 text-center\">\n {dayNames.map((day, idx) => (\n <div\n key={idx}\n className=\"text-xs font-medium uppercase opacity-60\"\n >\n {day}\n </div>\n ))}\n </div>\n\n {/* Calendar Grid - Week view on mobile when collapsed */}\n {isCollapsed && (\n <div className=\"grid grid-cols-7 gap-1 @md:hidden\">\n {weekGrid.map(({ date, isToday }, idx) => (\n <DayButton\n key={idx}\n date={date}\n isToday={isToday}\n isSelected={\n selectedDate?.toDateString() === date.toDateString()\n }\n hasEvents={hasEventsOnDate(date, events)}\n accentColor={accentColor}\n onClick={() => setSelectedDate(date)}\n />\n ))}\n </div>\n )}\n\n {/* Month view (always on desktop, when not collapsed on mobile) */}\n <div\n className={`grid grid-cols-7 gap-1 ${isCollapsed ? \"hidden @md:grid\" : \"grid\"}`}\n >\n {monthGrid.map((day, idx) => {\n if (!day.date) {\n return <div key={idx} className=\"aspect-square\" />;\n }\n\n return (\n <DayButton\n key={idx}\n date={day.date}\n isToday={day.isToday}\n isSelected={\n selectedDate?.toDateString() === day.date.toDateString()\n }\n hasEvents={hasEventsOnDate(day.date, events)}\n accentColor={accentColor}\n onClick={() => setSelectedDate(day.date)}\n extraClassName=\"p-2 @md:m-3\"\n />\n );\n })}\n </div>\n\n {/* Mobile collapse toggle */}\n <button\n onClick={() => setIsCollapsed(!isCollapsed)}\n className={`ml-2 flex w-full items-center justify-center gap-3 rounded-full p-1 text-${textColor}/50 @md:hidden`}\n aria-label={\n isCollapsed ? \"Expand calendar\" : \"Collapse calendar\"\n }\n >\n <div className={`h-1 w-1/2 bg-${textColor}/50`} />\n {isCollapsed ? (\n <FontAwesomeIcon icon={faChevronsDown} />\n ) : (\n <FontAwesomeIcon icon={faChevronsUp} />\n )}\n <div className={`h-1 w-1/2 bg-${textColor}/50`} />\n </button>\n </div>\n\n {/* Events Panel */}\n <div className=\"border-muted mt-4 pt-4 @md:mt-0 @md:w-56 @md:border-l @md:pt-0 @md:pl-6\">\n <div className=\"mb-2 text-sm font-medium opacity-60\">\n {selectedDate\n ? selectedDate.toLocaleDateString(\"en-US\", {\n weekday: \"long\",\n month: \"short\",\n day: \"numeric\",\n })\n : \"Select a date\"}\n </div>\n\n {selectedDateEvents.length > 0 ? (\n <div className=\"flex flex-col gap-3\">\n {selectedDateEvents.map((event, idx) => (\n <EventItem\n key={`${event.id}-${idx}`}\n {...event}\n accentColor={`var(--${accentColor})`}\n />\n ))}\n </div>\n ) : (\n <div className=\"text-sm opacity-50\">No events</div>\n )}\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport const calendarWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"CalendarWidget\",\n displayName: \"Calendar Widget\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [\n // Styling Tab - Title Group\n {\n key: \"titleEnabled\",\n label: \"Widget Title\",\n type: \"boolean\",\n description: \"Enable the title displayed above the calendar\",\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 calendar\",\n defaultValue: \"Calendar\",\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 // Styling Tab - Design Group\n {\n type: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Background for the calendar 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 calendar content\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getColorField({\n key: \"accentColor\",\n label: \"Accent Color\",\n description: \"Color used for today highlight and event indicators\",\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 calendar 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 calendar container\",\n defaultValue: \"md\",\n tab: \"styling\",\n group: \"Design\",\n }),\n ],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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I;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;uDACgB;GACf,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;oDACY;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EAGF;GACE,MAAM;GACN,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;oDACa;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;oDACY;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF;GACE,KAAK;GACL,MAAM;GACN,OAAO;GACP,KAAK;GACL,OAAO;GACR;sDACe;GACd,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;2DACmB;GACnB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACH;CACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CalendarWidget-Dk8O2NRC.mjs","names":[],"sources":["../src/hooks/use-calendar-events.preview.ts","../src/hooks/use-calendar-events.ts","../src/widgets/CalendarWidget.tsx"],"sourcesContent":["import type { CalendarEvent } from \"./use-calendar-events.types\";\n\nconst today = new Date();\nconst inTwoDays = new Date(today);\ninTwoDays.setDate(inTwoDays.getDate() + 2);\n\nfunction toISO(date: Date, hours: number, minutes = 0): string {\n const d = new Date(date);\n d.setHours(hours, minutes, 0, 0);\n return d.toISOString();\n}\n\nfunction toDateString(date: Date): string {\n return date.toISOString().split(\"T\")[0]!;\n}\n\nexport const PREVIEW_DATA: CalendarEvent[] = [\n {\n id: 1,\n title: \"Team Strategy Call\",\n start: toISO(today, 9, 0),\n end: toISO(today, 10, 0),\n status: \"active\",\n color: \"#4f46e5\",\n },\n {\n id: 2,\n title: \"Product Launch Webinar\",\n start: toISO(today, 14, 0),\n end: toISO(today, 15, 30),\n status: \"active\",\n color: \"#0891b2\",\n venue: \"Zoom\",\n },\n {\n id: 3,\n title: \"Annual Leadership Summit\",\n start: `${toDateString(inTwoDays)}T00:00:00.000Z`,\n end: `${toDateString(inTwoDays)}T23:59:59.000Z`,\n status: \"active\",\n color: \"#059669\",\n isAllDay: true,\n venue: \"Convention Center\",\n },\n];\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useDataSourceConfig } from \"@fluid-app/rep-core/data-sources/context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/rep-core/data-sources/preview-context\";\nimport { PREVIEW_DATA } from \"./use-calendar-events.preview\";\n\nimport type { CalendarEvent } from \"./use-calendar-events.types\";\n\nexport type { CalendarEvent } from \"./use-calendar-events.types\";\n\nexport function useCalendarEvents() {\n const { baseUrl, getApiHeaders } = useDataSourceConfig();\n const { isPreview } = useWidgetPreviewContext();\n\n return useQuery({\n queryKey: [\n \"rep-widget-use\",\n \"calendar-events\",\n isPreview ? \"preview\" : baseUrl,\n ] as const,\n queryFn: async ({ signal }): Promise<CalendarEvent[]> => {\n const url = baseUrl\n ? `${baseUrl}/company/events?status=active`\n : \"/company/events?status=active\";\n const response = await fetch(url, {\n headers: {\n \"content-type\": \"application/json\",\n ...getApiHeaders?.(),\n },\n signal,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch calendar events: ${response.status}`);\n }\n\n return response.json();\n },\n enabled: !isPreview,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\n });\n}\n","import { useState, useMemo, type ComponentProps } from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n FontSizeOptions,\n PaddingOptions,\n} from \"@fluid-app/rep-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/rep-core/registries\";\nimport {\n getBorderRadiusField,\n getColorField,\n getFontSizeField,\n getPaddingField,\n} from \"../core/fields\";\nimport {\n useCalendarEvents,\n type CalendarEvent,\n} from \"../hooks/use-calendar-events\";\nimport { ErrorState } from \"../components/error-state\";\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport {\n faChevronLeft,\n faChevronRight,\n faChevronsDown,\n faChevronsUp,\n} from \"@fortawesome/pro-regular-svg-icons\";\n\n// Utility to get days in a month\nconst getDaysInMonth = (year: number, month: number) => {\n return new Date(year, month + 1, 0).getDate();\n};\n\n// Utility to get the first day of month (0 = Sunday, 1 = Monday, etc.)\nconst getFirstDayOfMonth = (year: number, month: number) => {\n return new Date(year, month, 1).getDay();\n};\n\n// Generate calendar grid for a month\nconst generateMonthGrid = (year: number, month: number) => {\n const daysInMonth = getDaysInMonth(year, month);\n const firstDay = getFirstDayOfMonth(year, month);\n const today = new Date();\n\n const days: Array<{\n date: Date | null;\n isToday: boolean;\n isCurrentMonth: boolean;\n }> = [];\n\n // Add empty slots for days before the 1st\n for (let i = 0; i < firstDay; i++) {\n days.push({ date: null, isToday: false, isCurrentMonth: false });\n }\n\n // Add days of the month\n for (let day = 1; day <= daysInMonth; day++) {\n const date = new Date(year, month, day);\n const isToday =\n date.getDate() === today.getDate() &&\n date.getMonth() === today.getMonth() &&\n date.getFullYear() === today.getFullYear();\n days.push({ date, isToday, isCurrentMonth: true });\n }\n\n return days;\n};\n\n// Generate week grid for current week\nconst generateWeekGrid = () => {\n const today = new Date();\n const currentDay = today.getDay();\n const days: Array<{\n date: Date;\n isToday: boolean;\n }> = [];\n\n for (let i = 0; i < 7; i++) {\n const date = new Date(today);\n date.setDate(today.getDate() - currentDay + i);\n days.push({\n date,\n isToday: date.toDateString() === today.toDateString(),\n });\n }\n\n return days;\n};\n\n// Get events for a specific date\nconst getEventsForDate = (date: Date, events: CalendarEvent[]) => {\n const dayStart = new Date(date);\n dayStart.setHours(0, 0, 0, 0);\n const dayEnd = new Date(date);\n dayEnd.setHours(23, 59, 59, 999);\n\n return events.filter((event) => {\n const eventStart = new Date(event.start);\n const eventEnd = new Date(event.end);\n return eventStart <= dayEnd && eventEnd >= dayStart;\n });\n};\n\n// Check if a date has events\nconst hasEventsOnDate = (date: Date | null, events: CalendarEvent[]) => {\n if (!date) return false;\n return getEventsForDate(date, events).length > 0;\n};\n\n// Event item component\nconst EventItem = ({\n title,\n start,\n end,\n color,\n isAllDay,\n accentColor,\n}: CalendarEvent & { accentColor?: string }) => {\n const startTime = new Date(start).toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n const endTime = new Date(end).toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n\n return (\n <div className=\"flex gap-x-2\">\n <div\n className=\"w-0.5 shrink-0 rounded-full\"\n style={{ backgroundColor: color || accentColor }}\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-sm font-medium\">{title}</div>\n <div className=\"text-xs opacity-60\">\n {isAllDay ? \"All day\" : `${startTime} - ${endTime}`}\n </div>\n </div>\n </div>\n );\n};\n\nconst dayNames = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\n\ntype DayButtonProps = {\n date: Date;\n isToday: boolean;\n isSelected: boolean;\n hasEvents: boolean;\n accentColor: string;\n onClick: () => void;\n extraClassName?: string;\n};\n\nfunction DayButton({\n date,\n isToday,\n isSelected,\n hasEvents,\n accentColor,\n onClick,\n extraClassName = \"\",\n}: DayButtonProps) {\n return (\n <button\n onClick={onClick}\n className={`relative flex aspect-square items-center justify-center rounded-full text-sm transition-colors ${\n isSelected\n ? `bg-${accentColor} text-${accentColor}-foreground`\n : isToday\n ? `bg-${accentColor}/30 text-${accentColor}-foreground hover:bg-${accentColor}/50`\n : `hover:bg-${accentColor}/50 hover:text-${accentColor}-foreground`\n } ${extraClassName}`}\n >\n {date.getDate()}\n {hasEvents && (\n <div\n className={`absolute bottom-1 h-1 w-1 rounded-full bg-${accentColor}`}\n />\n )}\n </button>\n );\n}\n\ntype CalendarWidgetProps = ComponentProps<\"div\"> & {\n // Title\n titleEnabled?: boolean;\n titleText?: string;\n titleFontSize?: FontSizeOptions;\n titleColor?: ColorOptions;\n\n // Styling\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n};\n\nexport function CalendarWidget({\n // Title defaults\n titleEnabled = true,\n titleText = \"Calendar\",\n titleFontSize = \"xl\",\n titleColor = \"foreground\",\n\n // Styling defaults\n background = {\n type: \"solid\",\n color: \"background\",\n },\n textColor = \"foreground\",\n accentColor = \"primary\",\n padding = 4,\n borderRadius = \"md\",\n\n className,\n ...props\n}: CalendarWidgetProps) {\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 const today = new Date();\n const [currentMonth, setCurrentMonth] = useState(today.getMonth());\n const [currentYear, setCurrentYear] = useState(today.getFullYear());\n const [isCollapsed, setIsCollapsed] = useState(false);\n const [selectedDate, setSelectedDate] = useState<Date | null>(today);\n\n const { data: events = [], isLoading, isError } = useCalendarEvents();\n\n // Generate calendar grids\n const monthGrid = useMemo(\n () => generateMonthGrid(currentYear, currentMonth),\n [currentYear, currentMonth],\n );\n const weekGrid = useMemo(() => generateWeekGrid(), []);\n\n // Get events for selected date\n const selectedDateEvents = useMemo(() => {\n if (!selectedDate) return [];\n return getEventsForDate(selectedDate, events);\n }, [selectedDate, events]);\n\n // Navigation handlers\n const goToPrevMonth = () => {\n if (currentMonth === 0) {\n setCurrentMonth(11);\n setCurrentYear(currentYear - 1);\n } else {\n setCurrentMonth(currentMonth - 1);\n }\n };\n\n const goToNextMonth = () => {\n if (currentMonth === 11) {\n setCurrentMonth(0);\n setCurrentYear(currentYear + 1);\n } else {\n setCurrentMonth(currentMonth + 1);\n }\n };\n\n const monthYearLabel = new Date(currentYear, currentMonth).toLocaleString(\n \"en-US\",\n {\n month: \"long\",\n year: \"numeric\",\n },\n );\n\n return (\n <div\n className={`@container overflow-hidden rounded-${borderRadius} bg-${backgroundColor} text-${textColor} ${className}`}\n style={{ backgroundImage }}\n {...props}\n >\n {/* Title */}\n {titleEnabled && titleText && (\n <div className={`p-${padding} pb-0`}>\n <h2 className={`text-${titleFontSize} font-bold text-${titleColor}`}>\n {titleText}\n </h2>\n </div>\n )}\n\n {/* Loading state */}\n {isLoading ? (\n <div\n className={`flex min-h-[300px] items-center justify-center p-${padding}`}\n >\n <div className=\"h-8 w-8 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n </div>\n ) : isError ? (\n /* Error state */\n <ErrorState />\n ) : (\n <div className={`p-${padding}`}>\n {/* Desktop: Side by side layout */}\n <div className=\"@md:flex @md:gap-6\">\n {/* Calendar Section */}\n <div className=\"@md:flex-none\">\n {/* Month/Year Header with Navigation */}\n <div className=\"mb-4 flex items-center justify-between\">\n <button\n onClick={goToPrevMonth}\n className=\"hover:bg-foreground/10 rounded-full p-1\"\n aria-label=\"Previous month\"\n >\n <FontAwesomeIcon icon={faChevronLeft} />\n </button>\n\n <span className=\"text-base font-semibold\">\n {monthYearLabel}\n </span>\n\n <button\n onClick={goToNextMonth}\n className=\"hover:bg-foreground/10 rounded-full p-1\"\n aria-label=\"Next month\"\n >\n <FontAwesomeIcon icon={faChevronRight} />\n </button>\n </div>\n\n {/* Day Headers */}\n <div className=\"mb-2 grid grid-cols-7 gap-1 text-center\">\n {dayNames.map((day, idx) => (\n <div\n key={idx}\n className=\"text-xs font-medium uppercase opacity-60\"\n >\n {day}\n </div>\n ))}\n </div>\n\n {/* Calendar Grid - Week view on mobile when collapsed */}\n {isCollapsed && (\n <div className=\"grid grid-cols-7 gap-1 @md:hidden\">\n {weekGrid.map(({ date, isToday }, idx) => (\n <DayButton\n key={idx}\n date={date}\n isToday={isToday}\n isSelected={\n selectedDate?.toDateString() === date.toDateString()\n }\n hasEvents={hasEventsOnDate(date, events)}\n accentColor={accentColor}\n onClick={() => setSelectedDate(date)}\n />\n ))}\n </div>\n )}\n\n {/* Month view (always on desktop, when not collapsed on mobile) */}\n <div\n className={`grid grid-cols-7 gap-1 ${isCollapsed ? \"hidden @md:grid\" : \"grid\"}`}\n >\n {monthGrid.map((day, idx) => {\n if (!day.date) {\n return <div key={idx} className=\"aspect-square\" />;\n }\n\n return (\n <DayButton\n key={idx}\n date={day.date}\n isToday={day.isToday}\n isSelected={\n selectedDate?.toDateString() === day.date.toDateString()\n }\n hasEvents={hasEventsOnDate(day.date, events)}\n accentColor={accentColor}\n onClick={() => setSelectedDate(day.date)}\n extraClassName=\"p-2 @md:m-3\"\n />\n );\n })}\n </div>\n\n {/* Mobile collapse toggle */}\n <button\n onClick={() => setIsCollapsed(!isCollapsed)}\n className={`ml-2 flex w-full items-center justify-center gap-3 rounded-full p-1 text-${textColor}/50 @md:hidden`}\n aria-label={\n isCollapsed ? \"Expand calendar\" : \"Collapse calendar\"\n }\n >\n <div className={`h-1 w-1/2 bg-${textColor}/50`} />\n {isCollapsed ? (\n <FontAwesomeIcon icon={faChevronsDown} />\n ) : (\n <FontAwesomeIcon icon={faChevronsUp} />\n )}\n <div className={`h-1 w-1/2 bg-${textColor}/50`} />\n </button>\n </div>\n\n {/* Events Panel */}\n <div className=\"border-muted mt-4 pt-4 @md:mt-0 @md:w-56 @md:border-l @md:pt-0 @md:pl-6\">\n <div className=\"mb-2 text-sm font-medium opacity-60\">\n {selectedDate\n ? selectedDate.toLocaleDateString(\"en-US\", {\n weekday: \"long\",\n month: \"short\",\n day: \"numeric\",\n })\n : \"Select a date\"}\n </div>\n\n {selectedDateEvents.length > 0 ? (\n <div className=\"flex flex-col gap-3\">\n {selectedDateEvents.map((event, idx) => (\n <EventItem\n key={`${event.id}-${idx}`}\n {...event}\n accentColor={`var(--${accentColor})`}\n />\n ))}\n </div>\n ) : (\n <div className=\"text-sm opacity-50\">No events</div>\n )}\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport const calendarWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"CalendarWidget\",\n displayName: \"Calendar Widget\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [\n // Styling Tab - Title Group\n {\n key: \"titleEnabled\",\n label: \"Widget Title\",\n type: \"boolean\",\n description: \"Enable the title displayed above the calendar\",\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 calendar\",\n defaultValue: \"Calendar\",\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 // Styling Tab - Design Group\n {\n type: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Background for the calendar 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 calendar content\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getColorField({\n key: \"accentColor\",\n label: \"Accent Color\",\n description: \"Color used for today highlight and event indicators\",\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 calendar 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 calendar container\",\n defaultValue: \"md\",\n tab: \"styling\",\n group: \"Design\",\n }),\n ],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"RecentActivityWidget-BgsjgQY5.cjs","names":["faCartShopping","faComment","faPlay","faUserPlus","faEye","faCalendar","faStar","faSquareCheck","faBell","faTrophy","faUser","FontAwesomeIcon","ErrorState"],"sources":["../src/hooks/use-activities.preview.ts","../src/hooks/use-activities.ts","../src/widgets/RecentActivityWidget.tsx"],"sourcesContent":["import type { Activity } from \"./use-activities.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 } from \"@tanstack/react-query\";\nimport { useDataSourceConfig } from \"@fluid-app/rep-core/data-sources/context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/rep-core/data-sources/preview-context\";\nimport { PREVIEW_DATA } from \"./use-activities.preview\";\nimport type {\n Activity,\n ActivityContact,\n ActivityVisitor,\n ApiActivity,\n} from \"./use-activities.types\";\n\nexport type {\n ActivityContact,\n ActivityVisitor,\n ActivitySlug,\n ApiActivity,\n Activity,\n} from \"./use-activities.types\";\n\ntype PaginationInfo = {\n current: number;\n previous: number | null;\n next: number | null;\n per_page: number;\n pages: number;\n count: number;\n};\n\ntype ApiResponse = [{ pagination: PaginationInfo }, { items: ApiActivity[] }];\n\nexport function useActivities() {\n const { baseUrl, getApiHeaders } = useDataSourceConfig();\n const { isPreview } = useWidgetPreviewContext();\n\n return useQuery({\n queryKey: [\n \"rep-widget-use\",\n \"activities\",\n isPreview ? \"preview\" : baseUrl,\n ] as const,\n queryFn: async ({ signal }): Promise<Activity[]> => {\n const url = baseUrl\n ? `${baseUrl}/v2/activities.json`\n : \"/v2/activities.json\";\n const response = await fetch(url, {\n headers: {\n \"content-type\": \"application/json\",\n ...getApiHeaders?.(),\n },\n signal,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch activities: ${response.status}`);\n }\n\n const data: ApiResponse = await response.json();\n return transformActivities(data);\n },\n enabled: !isPreview,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\n });\n}\n\n// Transform and format helpers\nfunction getUserName(\n contact: ActivityContact | null,\n visitor: ActivityVisitor | null,\n): string {\n if (contact?.full_name) {\n return contact.full_name;\n }\n if (contact?.first_name && contact?.last_name) {\n return `${contact.first_name} ${contact.last_name}`;\n }\n if (visitor?.city && visitor?.state) {\n return `Visitor from ${visitor.city}, ${visitor.state}`;\n }\n return \"Unknown Visitor\";\n}\n\nfunction transformActivities(rawData: ApiResponse): Activity[] {\n const itemsObj = rawData[1];\n if (!itemsObj?.items) return [];\n\n return itemsObj.items\n .map((activity) => ({\n id: activity.id,\n userName: getUserName(activity.contact, activity.visitor),\n avatarUrl: activity.contact?.avatar_url ?? null,\n activityType: formatActivityType(activity.slug),\n targetName: activity.description || activity.title,\n timestamp: activity.created_at,\n slug: activity.slug,\n }))\n .sort(\n (a, b) =>\n new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),\n );\n}\n\nfunction formatActivityType(slug: string): string {\n return slug\n .split(\"_\")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \");\n}\n","import { useMemo, type ComponentProps } from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n FontSizeOptions,\n PaddingOptions,\n} from \"@fluid-app/rep-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/rep-core/registries\";\nimport {\n getBorderRadiusField,\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 { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport type { IconDefinition } from \"@fortawesome/fontawesome-svg-core\";\nimport {\n faBell,\n faCalendar,\n faSquareCheck,\n faEye,\n faComment,\n faPlay,\n faCartShopping,\n faStar,\n faTrophy,\n faUser,\n faUserPlus,\n} from \"@fortawesome/pro-regular-svg-icons\";\n\n// Format timestamp to time string\nconst formatTimestamp = (timestamp: string): string => {\n const date = new Date(timestamp);\n return date.toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true,\n });\n};\n\n// Format date for grouping header\nconst formatDateHeader = (timestamp: string): string => {\n const date = new Date(timestamp);\n return date.toLocaleDateString(\"en-US\", {\n month: \"long\",\n day: \"numeric\",\n });\n};\n\n// Get date string for grouping (without time)\nconst getDateKey = (timestamp: string): string => {\n const date = new Date(timestamp);\n return date.toISOString().split(\"T\")[0] ?? timestamp;\n};\n\n// Group activities by date\nconst groupActivitiesByDate = (\n activities: Activity[],\n): Map<string, Activity[]> => {\n const grouped = new Map<string, Activity[]>();\n\n for (const activity of activities) {\n const dateKey = getDateKey(activity.timestamp);\n const existing = grouped.get(dateKey);\n if (existing) {\n existing.push(activity);\n } else {\n grouped.set(dateKey, [activity]);\n }\n }\n\n return grouped;\n};\n\n// Activity slug to icon mapping\nconst ACTIVITY_ICON_MAP: Record<ActivitySlug, IconDefinition> = {\n // Orders/Cart\n order_placed: faCartShopping,\n abandoned_cart: faCartShopping,\n cart_items_added: faCartShopping,\n new_cart_items_added: faCartShopping,\n // Messages\n direct_message: faComment,\n comment_reply: faComment,\n message_received: faComment,\n message_sent: faComment,\n // Video\n video: faPlay,\n video_complete: faPlay,\n video_contact: faPlay,\n video_complete_contact: faPlay,\n // Leads\n new_lead: faUserPlus,\n page_views_contact: faUserPlus,\n smart_link_clicked: faUserPlus,\n // Page Views\n page_views: faEye,\n // Events\n upcoming_event: faCalendar,\n // Reviews\n review_left: faStar,\n // Tasks\n tasks: faSquareCheck,\n // Announcements\n announcements: faBell,\n // Fantasy\n fantasy_point: faTrophy,\n};\n\nconst getActivityIcon = (slug: ActivitySlug) =>\n ACTIVITY_ICON_MAP[slug] ?? faUser;\n\n// Activity feed item component\ntype ActivityFeedItemProps = {\n activity: Activity;\n accentColor: ColorOptions;\n textColor: ColorOptions;\n};\n\nfunction ActivityFeedItem({\n activity,\n accentColor,\n textColor,\n}: ActivityFeedItemProps) {\n const icon = getActivityIcon(activity.slug);\n\n return (\n <div className=\"flex w-full items-start gap-1.5\">\n {/* Avatar */}\n <div className=\"shrink-0\">\n {activity.avatarUrl ? (\n <div className=\"relative size-8 overflow-hidden rounded-full\">\n <img\n src={activity.avatarUrl}\n alt={activity.userName}\n className=\"size-full object-cover\"\n />\n <div className=\"border-foreground/[0.08] absolute inset-0 rounded-full border\" />\n </div>\n ) : (\n <div\n className={`bg-muted flex size-8 items-center justify-center rounded-full`}\n >\n <FontAwesomeIcon\n icon={icon}\n className={`size-3.5 text-${textColor} opacity-60`}\n />\n </div>\n )}\n </div>\n\n {/* Content */}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex w-full items-center gap-1.5\">\n <p\n className={`flex-1 text-base font-semibold text-${textColor} truncate`}\n >\n {activity.activityType}\n </p>\n <p className={`text-xs text-${textColor} shrink-0 opacity-50`}>\n {formatTimestamp(activity.timestamp)}\n </p>\n </div>\n <p className={`text-sm text-${textColor} opacity-80`}>\n <span className={`font-medium text-${accentColor}`}>\n {activity.userName}\n </span>{\" \"}\n {activity.targetName}\n </p>\n </div>\n </div>\n );\n}\n\ntype RecentActivityWidgetProps = ComponentProps<\"div\"> & {\n // Title\n titleEnabled?: boolean;\n titleText?: string;\n titleFontSize?: FontSizeOptions;\n titleColor?: ColorOptions;\n\n // Styling\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n\n // Activity settings\n maxItemsToShow?: number;\n};\n\nexport function RecentActivityWidget({\n // Title defaults\n titleEnabled = true,\n titleText = \"Recent Activity\",\n titleFontSize = \"lg\",\n titleColor = \"foreground\",\n\n // Styling defaults\n background = {\n type: \"solid\",\n color: \"background\",\n },\n textColor = \"foreground\",\n accentColor = \"primary\",\n padding = 4,\n borderRadius = \"md\",\n\n // Activity defaults\n maxItemsToShow = 5,\n\n className,\n ...props\n}: RecentActivityWidgetProps) {\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 const { data: activities = [], isLoading, isError } = useActivities();\n\n const groupedActivities = useMemo(\n () => groupActivitiesByDate(activities),\n [activities],\n );\n\n const totalCount = Math.min(activities.length, maxItemsToShow);\n\n // Get activities to display (limited by maxItemsToShow)\n const activitiesToShow = useMemo(() => {\n const result: { date: string; items: Activity[] }[] = [];\n let count = 0;\n\n for (const [dateKey, items] of groupedActivities) {\n if (count >= maxItemsToShow) break;\n\n const remainingSlots = maxItemsToShow - count;\n const itemsToAdd = items.slice(0, remainingSlots);\n\n if (itemsToAdd.length > 0) {\n result.push({\n date: formatDateHeader(items[0]?.timestamp ?? dateKey),\n items: itemsToAdd,\n });\n count += itemsToAdd.length;\n }\n }\n\n return result;\n }, [groupedActivities, maxItemsToShow]);\n\n return (\n <div\n className={`@container overflow-hidden rounded-${borderRadius} bg-${backgroundColor} text-${textColor} border-muted border ${className}`}\n style={{ backgroundImage }}\n {...props}\n >\n <div className={`p-${padding} flex flex-col gap-2`}>\n {/* Header */}\n {titleEnabled && titleText && (\n <div className=\"flex w-full items-start gap-2\">\n <h2\n className={`flex-1 text-${titleFontSize} font-bold text-${titleColor}`}\n >\n {titleText}\n </h2>\n {!isLoading && (\n <span\n className={`text-4xl font-bold text-${textColor} leading-none`}\n >\n {totalCount.toString().padStart(2, \"0\")}\n </span>\n )}\n </div>\n )}\n\n {/* Loading state */}\n {isLoading ? (\n <div className=\"flex min-h-[200px] items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n </div>\n ) : isError ? (\n /* Error state */\n <ErrorState />\n ) : activities.length === 0 ? (\n /* Empty state */\n <div className=\"flex min-h-[200px] flex-col items-center justify-center gap-2\">\n <FontAwesomeIcon\n icon={faUser}\n className={`size-12 text-${textColor} opacity-30`}\n />\n <p\n className={`text-base font-semibold text-${textColor} opacity-50`}\n >\n No Activity To Report\n </p>\n <p className={`text-sm text-${textColor} opacity-40`}>\n You'll Do Great!\n </p>\n </div>\n ) : (\n /* Activity list */\n <div className=\"flex flex-col gap-4\">\n {activitiesToShow.map((group, groupIndex) => (\n <div key={groupIndex} className=\"flex flex-col gap-4\">\n {/* Date header */}\n <p className={`text-base font-semibold text-${textColor}`}>\n {group.date}\n </p>\n\n {/* Activity items */}\n <div className=\"flex flex-col gap-4\">\n {group.items.map((activity) => (\n <ActivityFeedItem\n key={activity.id}\n activity={activity}\n accentColor={accentColor}\n textColor={textColor}\n />\n ))}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n\nexport const recentActivityWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"RecentActivityWidget\",\n displayName: \"Recent Activity Widget\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [\n // Styling Tab - Title Group\n {\n key: \"titleEnabled\",\n label: \"Widget Title\",\n type: \"boolean\",\n description: \"Enable the title displayed above the activity 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: \"Recent 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 // Styling Tab - Design Group\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: \"Color used for links and highlights\",\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\n // Styling Tab - Display Group\n {\n key: \"maxItemsToShow\",\n label: \"Max Items\",\n type: \"number\",\n description: \"Maximum number of activity items to display\",\n defaultValue: 5,\n tab: \"styling\",\n group: \"Display\",\n },\n ],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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P,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;oDACY;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EAGF;GACE,MAAM;GACN,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;oDACa;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;oDACY;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF;GACE,KAAK;GACL,MAAM;GACN,OAAO;GACP,KAAK;GACL,OAAO;GACR;sDACe;GACd,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;2DACmB;GACnB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EAGF;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACF;CACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"RecentActivityWidget-ComwTgR9.mjs","names":[],"sources":["../src/hooks/use-activities.preview.ts","../src/hooks/use-activities.ts","../src/widgets/RecentActivityWidget.tsx"],"sourcesContent":["import type { Activity } from \"./use-activities.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 } from \"@tanstack/react-query\";\nimport { useDataSourceConfig } from \"@fluid-app/rep-core/data-sources/context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/rep-core/data-sources/preview-context\";\nimport { PREVIEW_DATA } from \"./use-activities.preview\";\nimport type {\n Activity,\n ActivityContact,\n ActivityVisitor,\n ApiActivity,\n} from \"./use-activities.types\";\n\nexport type {\n ActivityContact,\n ActivityVisitor,\n ActivitySlug,\n ApiActivity,\n Activity,\n} from \"./use-activities.types\";\n\ntype PaginationInfo = {\n current: number;\n previous: number | null;\n next: number | null;\n per_page: number;\n pages: number;\n count: number;\n};\n\ntype ApiResponse = [{ pagination: PaginationInfo }, { items: ApiActivity[] }];\n\nexport function useActivities() {\n const { baseUrl, getApiHeaders } = useDataSourceConfig();\n const { isPreview } = useWidgetPreviewContext();\n\n return useQuery({\n queryKey: [\n \"rep-widget-use\",\n \"activities\",\n isPreview ? \"preview\" : baseUrl,\n ] as const,\n queryFn: async ({ signal }): Promise<Activity[]> => {\n const url = baseUrl\n ? `${baseUrl}/v2/activities.json`\n : \"/v2/activities.json\";\n const response = await fetch(url, {\n headers: {\n \"content-type\": \"application/json\",\n ...getApiHeaders?.(),\n },\n signal,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch activities: ${response.status}`);\n }\n\n const data: ApiResponse = await response.json();\n return transformActivities(data);\n },\n enabled: !isPreview,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\n });\n}\n\n// Transform and format helpers\nfunction getUserName(\n contact: ActivityContact | null,\n visitor: ActivityVisitor | null,\n): string {\n if (contact?.full_name) {\n return contact.full_name;\n }\n if (contact?.first_name && contact?.last_name) {\n return `${contact.first_name} ${contact.last_name}`;\n }\n if (visitor?.city && visitor?.state) {\n return `Visitor from ${visitor.city}, ${visitor.state}`;\n }\n return \"Unknown Visitor\";\n}\n\nfunction transformActivities(rawData: ApiResponse): Activity[] {\n const itemsObj = rawData[1];\n if (!itemsObj?.items) return [];\n\n return itemsObj.items\n .map((activity) => ({\n id: activity.id,\n userName: getUserName(activity.contact, activity.visitor),\n avatarUrl: activity.contact?.avatar_url ?? null,\n activityType: formatActivityType(activity.slug),\n targetName: activity.description || activity.title,\n timestamp: activity.created_at,\n slug: activity.slug,\n }))\n .sort(\n (a, b) =>\n new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),\n );\n}\n\nfunction formatActivityType(slug: string): string {\n return slug\n .split(\"_\")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \");\n}\n","import { useMemo, type ComponentProps } from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n FontSizeOptions,\n PaddingOptions,\n} from \"@fluid-app/rep-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/rep-core/registries\";\nimport {\n getBorderRadiusField,\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 { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport type { IconDefinition } from \"@fortawesome/fontawesome-svg-core\";\nimport {\n faBell,\n faCalendar,\n faSquareCheck,\n faEye,\n faComment,\n faPlay,\n faCartShopping,\n faStar,\n faTrophy,\n faUser,\n faUserPlus,\n} from \"@fortawesome/pro-regular-svg-icons\";\n\n// Format timestamp to time string\nconst formatTimestamp = (timestamp: string): string => {\n const date = new Date(timestamp);\n return date.toLocaleTimeString(\"en-US\", {\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true,\n });\n};\n\n// Format date for grouping header\nconst formatDateHeader = (timestamp: string): string => {\n const date = new Date(timestamp);\n return date.toLocaleDateString(\"en-US\", {\n month: \"long\",\n day: \"numeric\",\n });\n};\n\n// Get date string for grouping (without time)\nconst getDateKey = (timestamp: string): string => {\n const date = new Date(timestamp);\n return date.toISOString().split(\"T\")[0] ?? timestamp;\n};\n\n// Group activities by date\nconst groupActivitiesByDate = (\n activities: Activity[],\n): Map<string, Activity[]> => {\n const grouped = new Map<string, Activity[]>();\n\n for (const activity of activities) {\n const dateKey = getDateKey(activity.timestamp);\n const existing = grouped.get(dateKey);\n if (existing) {\n existing.push(activity);\n } else {\n grouped.set(dateKey, [activity]);\n }\n }\n\n return grouped;\n};\n\n// Activity slug to icon mapping\nconst ACTIVITY_ICON_MAP: Record<ActivitySlug, IconDefinition> = {\n // Orders/Cart\n order_placed: faCartShopping,\n abandoned_cart: faCartShopping,\n cart_items_added: faCartShopping,\n new_cart_items_added: faCartShopping,\n // Messages\n direct_message: faComment,\n comment_reply: faComment,\n message_received: faComment,\n message_sent: faComment,\n // Video\n video: faPlay,\n video_complete: faPlay,\n video_contact: faPlay,\n video_complete_contact: faPlay,\n // Leads\n new_lead: faUserPlus,\n page_views_contact: faUserPlus,\n smart_link_clicked: faUserPlus,\n // Page Views\n page_views: faEye,\n // Events\n upcoming_event: faCalendar,\n // Reviews\n review_left: faStar,\n // Tasks\n tasks: faSquareCheck,\n // Announcements\n announcements: faBell,\n // Fantasy\n fantasy_point: faTrophy,\n};\n\nconst getActivityIcon = (slug: ActivitySlug) =>\n ACTIVITY_ICON_MAP[slug] ?? faUser;\n\n// Activity feed item component\ntype ActivityFeedItemProps = {\n activity: Activity;\n accentColor: ColorOptions;\n textColor: ColorOptions;\n};\n\nfunction ActivityFeedItem({\n activity,\n accentColor,\n textColor,\n}: ActivityFeedItemProps) {\n const icon = getActivityIcon(activity.slug);\n\n return (\n <div className=\"flex w-full items-start gap-1.5\">\n {/* Avatar */}\n <div className=\"shrink-0\">\n {activity.avatarUrl ? (\n <div className=\"relative size-8 overflow-hidden rounded-full\">\n <img\n src={activity.avatarUrl}\n alt={activity.userName}\n className=\"size-full object-cover\"\n />\n <div className=\"border-foreground/[0.08] absolute inset-0 rounded-full border\" />\n </div>\n ) : (\n <div\n className={`bg-muted flex size-8 items-center justify-center rounded-full`}\n >\n <FontAwesomeIcon\n icon={icon}\n className={`size-3.5 text-${textColor} opacity-60`}\n />\n </div>\n )}\n </div>\n\n {/* Content */}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex w-full items-center gap-1.5\">\n <p\n className={`flex-1 text-base font-semibold text-${textColor} truncate`}\n >\n {activity.activityType}\n </p>\n <p className={`text-xs text-${textColor} shrink-0 opacity-50`}>\n {formatTimestamp(activity.timestamp)}\n </p>\n </div>\n <p className={`text-sm text-${textColor} opacity-80`}>\n <span className={`font-medium text-${accentColor}`}>\n {activity.userName}\n </span>{\" \"}\n {activity.targetName}\n </p>\n </div>\n </div>\n );\n}\n\ntype RecentActivityWidgetProps = ComponentProps<\"div\"> & {\n // Title\n titleEnabled?: boolean;\n titleText?: string;\n titleFontSize?: FontSizeOptions;\n titleColor?: ColorOptions;\n\n // Styling\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n\n // Activity settings\n maxItemsToShow?: number;\n};\n\nexport function RecentActivityWidget({\n // Title defaults\n titleEnabled = true,\n titleText = \"Recent Activity\",\n titleFontSize = \"lg\",\n titleColor = \"foreground\",\n\n // Styling defaults\n background = {\n type: \"solid\",\n color: \"background\",\n },\n textColor = \"foreground\",\n accentColor = \"primary\",\n padding = 4,\n borderRadius = \"md\",\n\n // Activity defaults\n maxItemsToShow = 5,\n\n className,\n ...props\n}: RecentActivityWidgetProps) {\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 const { data: activities = [], isLoading, isError } = useActivities();\n\n const groupedActivities = useMemo(\n () => groupActivitiesByDate(activities),\n [activities],\n );\n\n const totalCount = Math.min(activities.length, maxItemsToShow);\n\n // Get activities to display (limited by maxItemsToShow)\n const activitiesToShow = useMemo(() => {\n const result: { date: string; items: Activity[] }[] = [];\n let count = 0;\n\n for (const [dateKey, items] of groupedActivities) {\n if (count >= maxItemsToShow) break;\n\n const remainingSlots = maxItemsToShow - count;\n const itemsToAdd = items.slice(0, remainingSlots);\n\n if (itemsToAdd.length > 0) {\n result.push({\n date: formatDateHeader(items[0]?.timestamp ?? dateKey),\n items: itemsToAdd,\n });\n count += itemsToAdd.length;\n }\n }\n\n return result;\n }, [groupedActivities, maxItemsToShow]);\n\n return (\n <div\n className={`@container overflow-hidden rounded-${borderRadius} bg-${backgroundColor} text-${textColor} border-muted border ${className}`}\n style={{ backgroundImage }}\n {...props}\n >\n <div className={`p-${padding} flex flex-col gap-2`}>\n {/* Header */}\n {titleEnabled && titleText && (\n <div className=\"flex w-full items-start gap-2\">\n <h2\n className={`flex-1 text-${titleFontSize} font-bold text-${titleColor}`}\n >\n {titleText}\n </h2>\n {!isLoading && (\n <span\n className={`text-4xl font-bold text-${textColor} leading-none`}\n >\n {totalCount.toString().padStart(2, \"0\")}\n </span>\n )}\n </div>\n )}\n\n {/* Loading state */}\n {isLoading ? (\n <div className=\"flex min-h-[200px] items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n </div>\n ) : isError ? (\n /* Error state */\n <ErrorState />\n ) : activities.length === 0 ? (\n /* Empty state */\n <div className=\"flex min-h-[200px] flex-col items-center justify-center gap-2\">\n <FontAwesomeIcon\n icon={faUser}\n className={`size-12 text-${textColor} opacity-30`}\n />\n <p\n className={`text-base font-semibold text-${textColor} opacity-50`}\n >\n No Activity To Report\n </p>\n <p className={`text-sm text-${textColor} opacity-40`}>\n You'll Do Great!\n </p>\n </div>\n ) : (\n /* Activity list */\n <div className=\"flex flex-col gap-4\">\n {activitiesToShow.map((group, groupIndex) => (\n <div key={groupIndex} className=\"flex flex-col gap-4\">\n {/* Date header */}\n <p className={`text-base font-semibold text-${textColor}`}>\n {group.date}\n </p>\n\n {/* Activity items */}\n <div className=\"flex flex-col gap-4\">\n {group.items.map((activity) => (\n <ActivityFeedItem\n key={activity.id}\n activity={activity}\n accentColor={accentColor}\n textColor={textColor}\n />\n ))}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n\nexport const recentActivityWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"RecentActivityWidget\",\n displayName: \"Recent Activity Widget\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [\n // Styling Tab - Title Group\n {\n key: \"titleEnabled\",\n label: \"Widget Title\",\n type: \"boolean\",\n description: \"Enable the title displayed above the activity 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: \"Recent 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 // Styling Tab - Design Group\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: \"Color used for links and highlights\",\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\n // Styling Tab - Display Group\n {\n key: \"maxItemsToShow\",\n label: \"Max Items\",\n type: \"number\",\n description: \"Maximum number of activity items to display\",\n defaultValue: 5,\n tab: \"styling\",\n group: \"Display\",\n },\n ],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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"}
|