diff --git a/assets/icons/headphones.svg b/assets/icons/headphones.svg
new file mode 100644
index 00000000..98b393ef
--- /dev/null
+++ b/assets/icons/headphones.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/monitor.svg b/assets/icons/monitor.svg
new file mode 100644
index 00000000..8d8c7af9
--- /dev/null
+++ b/assets/icons/monitor.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/shopping-cart.svg b/assets/icons/shopping-cart.svg
new file mode 100644
index 00000000..fedbbae0
--- /dev/null
+++ b/assets/icons/shopping-cart.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/smartphone.svg b/assets/icons/smartphone.svg
new file mode 100644
index 00000000..bb785ab5
--- /dev/null
+++ b/assets/icons/smartphone.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/star-solid.svg b/assets/icons/star-solid.svg
new file mode 100644
index 00000000..02f6e1ba
--- /dev/null
+++ b/assets/icons/star-solid.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/assets/icons/trending-up.svg b/assets/icons/trending-up.svg
new file mode 100644
index 00000000..b44aef1e
--- /dev/null
+++ b/assets/icons/trending-up.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/wallet.svg b/assets/icons/wallet.svg
new file mode 100644
index 00000000..9d39e59a
--- /dev/null
+++ b/assets/icons/wallet.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/samples/mediaco/CustomListView/ActivityListView.scss b/src/samples/mediaco/CustomListView/ActivityListView.scss
new file mode 100644
index 00000000..c1ff3d00
--- /dev/null
+++ b/src/samples/mediaco/CustomListView/ActivityListView.scss
@@ -0,0 +1,395 @@
+.alv-root {
+ color: #111827;
+ max-width: 100%;
+}
+
+.header-container {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ margin-bottom: 10px;
+}
+
+.alv-title {
+ margin: 0;
+ line-height: 1.1;
+ font-size: 2rem;
+ font-weight: 400;
+ color: #222;
+ position: relative;
+ display: inline-block;
+ padding-bottom: 8px;
+
+ &::after {
+ content: '';
+ position: absolute;
+ left: 0;
+ bottom: 0;
+ width: 100%;
+ height: 4px;
+ background: linear-gradient(to right, #a41a61, #ff4fc3);
+ border-radius: 2px;
+ }
+}
+
+.alv-headerBtn {
+ cursor: pointer;
+ text-align: right;
+ margin-top: 10px;
+ color: #6750a4;
+
+ &:hover {
+ text-decoration: underline;
+ }
+}
+
+.alv-card {
+ background: #ffffff;
+ border: 1px solid rgba(17, 24, 39, 0.08);
+ border-radius: 18px;
+ box-shadow: 0 10px 22px rgba(17, 24, 39, 0.1);
+ overflow: hidden;
+}
+
+.alv-row {
+ position: relative;
+ display: grid;
+ grid-template-columns: 52px 1fr auto;
+ align-items: center;
+ column-gap: 14px;
+ padding: 16px 18px;
+
+ &:not(:last-child)::after {
+ content: '';
+ position: absolute;
+ bottom: 0;
+ left: 20px;
+ right: 16px;
+ height: 1px;
+ background-color: #e5e7eb;
+ }
+
+ &:hover .alv-iconCircle {
+ transform: scale(1.1);
+ }
+}
+
+.alv-rowClickable {
+ cursor: pointer;
+
+ &:hover {
+ background: rgba(17, 24, 39, 0.02);
+ }
+}
+
+/* Icon circle */
+.alv-iconCircle {
+ width: 44px;
+ height: 44px;
+ border-radius: 15px;
+ display: grid;
+ place-items: center;
+ flex: none;
+}
+
+/*
icon */
+.alv-iconImg {
+ width: 20px;
+ height: 20px;
+ object-fit: contain;
+ display: block;
+}
+
+/* Background colors for each icon */
+.bg-0 {
+ background-color: #ede9fe;
+} /* Light purple */
+.bg-1 {
+ background-color: #fce7f3;
+} /* Light pink */
+.bg-2 {
+ background-color: #e0f2fe;
+} /* Light blue */
+.bg-3 {
+ background-color: #ffedd5;
+} /* Light orange */
+.bg-4 {
+ background-color: #f3e8ff;
+} /* Lavender */
+.bg-5 {
+ background-color: #d1fae5;
+} /* Light green */
+
+.color-0 {
+ filter: brightness(0) saturate(100%) invert(20%) sepia(80%) hue-rotate(250deg) saturate(500%);
+} /* Dark purple */
+.color-1 {
+ filter: brightness(0) saturate(100%) invert(20%) sepia(80%) hue-rotate(320deg) saturate(500%);
+} /* Dark pink */
+.color-2 {
+ filter: brightness(0) saturate(100%) invert(20%) sepia(80%) hue-rotate(190deg) saturate(500%);
+} /* Dark blue */
+.color-3 {
+ filter: brightness(0) saturate(100%) invert(20%) sepia(80%) hue-rotate(25deg) saturate(500%);
+} /* Dark orange */
+.color-4 {
+ filter: brightness(0) saturate(100%) invert(35%) sepia(55%) hue-rotate(255deg) saturate(420%);
+} /* Dark Lavender */
+.color-5 {
+ filter: brightness(0) saturate(100%) invert(20%) sepia(80%) hue-rotate(90deg) saturate(500%);
+} /* Dark green */
+
+.alv-content {
+ min-width: 0;
+}
+
+.extra-content {
+ height: 48px;
+ display: flex;
+ align-items: center;
+}
+
+.star {
+ filter: brightness(0) saturate(100%) invert(60%) sepia(92%) saturate(748%) hue-rotate(1deg) brightness(101%);
+ height: 16px;
+}
+
+.alv-topLine {
+ display: flex;
+ align-items: baseline;
+ gap: 8px;
+ flex-wrap: wrap;
+}
+
+.alv-itemTitle {
+ font-size: 16px;
+ font-weight: 500;
+}
+
+.alv-dot {
+ color: rgba(17, 24, 39, 0.35);
+ font-weight: 700;
+}
+
+.alv-time {
+ font-size: 12px;
+ font-weight: 450;
+ color: rgba(17, 24, 39, 0.45);
+}
+
+.alv-desc {
+ margin: 0;
+ font-size: 14px;
+ line-height: 1.35;
+ color: rgba(17, 24, 39, 0.7);
+}
+
+.alv-divider {
+ position: absolute;
+ left: 84px;
+ right: 18px;
+ bottom: 0;
+ height: 1px;
+ background: rgba(17, 24, 39, 0.1);
+}
+
+.alv-modalOverlay {
+ position: fixed;
+ inset: 0;
+ z-index: 9999;
+ display: grid;
+ place-items: center;
+}
+
+.alv-modalBackdrop {
+ position: absolute;
+ inset: 0;
+ background: rgba(17, 24, 39, 0.5);
+}
+
+.alv-modalPanel {
+ position: relative;
+ width: min(920px, calc(100vw - 48px));
+ height: min(560px, calc(100vh - 72px));
+ background: #ffffff;
+ border-radius: 14px;
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25);
+ overflow: hidden;
+ border: 1px solid rgba(17, 24, 39, 0.12);
+}
+
+.alv-modalShell {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+.alv-modalHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ padding: 18px 18px 14px 18px;
+}
+
+.alv-modalTitle {
+ margin: 0;
+ font-size: 28px;
+ font-weight: 500;
+ color: #111827;
+ line-height: 1.1;
+}
+
+.alv-modalSubtitle {
+ margin-top: 6px;
+ font-size: 12px;
+ font-weight: 400;
+ color: rgba(17, 24, 39, 0.55);
+}
+
+.alv-modalCloseBtn {
+ border: none;
+ background: rgba(17, 24, 39, 0.06);
+ color: #111827;
+ width: 34px;
+ height: 34px;
+ border-radius: 999px;
+ display: grid;
+ place-items: center;
+ cursor: pointer;
+
+ &:hover {
+ background: rgba(17, 24, 39, 0.1);
+ }
+}
+
+.alv-modalDivider {
+ height: 1px;
+ background: rgba(17, 24, 39, 0.12);
+}
+
+.alv-modalBody {
+ padding: 18px;
+ overflow: auto;
+}
+
+/* Tiles grid */
+.alv-tilesGrid {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 18px;
+
+ /* Responsive: 1 column on narrow screens */
+ @media (max-width: 640px) {
+ grid-template-columns: 1fr;
+ }
+}
+
+/* Tile card */
+.alv-tile {
+ text-align: left;
+ width: 100%;
+ border: 1px solid rgba(17, 24, 39, 0.14);
+ background: rgba(255, 255, 255, 0.35);
+ border-radius: 14px;
+ padding: 16px;
+ display: grid;
+ grid-template-columns: 52px 1fr;
+ column-gap: 14px;
+ align-items: center;
+ box-shadow: 0 6px 14px rgba(17, 24, 39, 0.12);
+ cursor: pointer;
+
+ &:hover {
+ background: rgba(255, 255, 255, 0.55);
+ transform: scale(1.01);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
+ }
+
+ &:focus {
+ outline: 2px solid rgba(214, 26, 138, 0.35);
+ outline-offset: 2px;
+ }
+}
+
+.alv-tileIconCircle {
+ width: 44px;
+ height: 44px;
+ border-radius: 12px;
+ display: grid;
+ place-items: center;
+}
+
+.alv-tileText {
+ min-width: 0;
+}
+
+.alv-tileTitle {
+ font-size: 16px;
+ font-weight: 500;
+ color: #111827;
+ margin-bottom: 4px;
+}
+
+.alv-tileDesc {
+ font-size: 12px;
+ line-height: 1.35;
+ color: rgba(17, 24, 39, 0.65);
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 1;
+ overflow: hidden;
+}
+
+/* --- Gallery Grid Modal Styles --- */
+.gallery-grid-container {
+ flex: 1;
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
+ gap: 20px;
+ padding: 24px;
+ overflow-y: auto;
+}
+
+.gallery-grid-item {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ border: 1px solid #e0e0e0;
+ border-radius: 1.5rem;
+ transition:
+ transform 0.2s,
+ box-shadow 0.2s;
+ aspect-ratio: 4 / 3;
+ overflow: hidden;
+ background-color: #000;
+ cursor: pointer;
+
+ &:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
+ }
+
+ img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ display: block;
+ }
+
+ p {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ margin: 0;
+ padding: 16px;
+ width: 100%;
+ box-sizing: border-box;
+ font-weight: 500;
+ color: #fff;
+ letter-spacing: 0.15px;
+ font-size: 16px;
+ line-height: 24px;
+ background: linear-gradient(transparent, rgba(0, 0, 0, 0.8));
+ }
+}
diff --git a/src/samples/mediaco/CustomListView/ActivityListView.tsx b/src/samples/mediaco/CustomListView/ActivityListView.tsx
new file mode 100644
index 00000000..e3151af4
--- /dev/null
+++ b/src/samples/mediaco/CustomListView/ActivityListView.tsx
@@ -0,0 +1,87 @@
+import './ActivityListView.scss';
+import { getImageSrc, getSDKStaticConentUrl } from './utils';
+
+interface ActivityItem {
+ rating?: number;
+ id?: string | number;
+ title: string;
+ description: string;
+ time: string;
+ iconSrc: string;
+ iconAlt?: string;
+ views?: string;
+ accent?: {
+ bg?: string;
+ fg?: string;
+ };
+}
+
+interface ActivityListViewProps {
+ title?: string;
+ items?: ActivityItem[];
+ referenceDataPage?: string;
+ showAllItems: () => void;
+ onItemClick?: (item: ActivityItem) => void;
+}
+
+export default function ActivityListView({ title, items = [], referenceDataPage, showAllItems }: ActivityListViewProps) {
+ return (
+
+
+
{title}
+
+
+
+ {items.slice(0, 5).map((item, idx) => (
+
+
+ {referenceDataPage === 'D_AccountHistoryList' && (
+

+ )}
+ {referenceDataPage === 'D_TrendingItemsList' &&
{idx + 1}}
+
+
+
+
+ {item.title}
+ {item.time && (
+
+ •
+
+ )}
+ {item.time}
+
+
+ {item.description}
+ {item.views && (
+
+ •
+
+ )}
+ {item.views}
+
+
+ {item.rating && (
+
+

+ {item.rating}
+
+ )}
+
+ ))}
+
+ {referenceDataPage === 'D_AccountHistoryList' && (
+
+ Show all
+
+ )}
+
+ );
+}
diff --git a/src/samples/mediaco/CustomListView/CarouselListView.scss b/src/samples/mediaco/CustomListView/CarouselListView.scss
new file mode 100644
index 00000000..f1dabc6a
--- /dev/null
+++ b/src/samples/mediaco/CustomListView/CarouselListView.scss
@@ -0,0 +1,118 @@
+.carousel-host-container {
+ width: 100%;
+ position: relative;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+}
+
+.header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 10px;
+ padding: 0 16px;
+
+ h2 {
+ margin: 0;
+ font-size: 20px;
+ font-weight: 500;
+ }
+}
+
+.carousel-scroll-area {
+ display: flex;
+ align-items: center;
+ width: 100%;
+ max-width: 100%;
+ padding: 0;
+ overflow-x: auto;
+ overflow-y: hidden;
+ scroll-behavior: auto;
+ box-sizing: border-box;
+ border-radius: 12px;
+
+ &::-webkit-scrollbar {
+ display: none;
+ }
+ scrollbar-width: none;
+}
+
+.card-wrapper {
+ flex: 0 0 200px;
+ height: 350px;
+ margin: 0 10px;
+ transition:
+ flex-basis 0.1s linear,
+ min-width 0.1s linear;
+ will-change: flex-basis, min-width;
+ min-width: 0;
+}
+
+.inner-material-card {
+ width: 100%;
+ height: 100%;
+ padding: 0 !important;
+ overflow: hidden;
+ position: relative;
+ background: #000;
+ border-radius: 8px;
+
+ img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ display: block;
+ }
+
+ .card-overlay {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ padding: 16px;
+ background: linear-gradient(transparent, rgba(0, 0, 0, 0.9));
+ color: white;
+
+ h3 {
+ margin: 0;
+ font-size: 16px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ }
+}
+
+.skeleton-shimmer {
+ width: 100%;
+ height: 100%;
+ background: #e0e0e0;
+ position: relative;
+ overflow: hidden;
+
+ &::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ transform: translateX(-100%);
+ background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.1) 50%, transparent 100%);
+ animation: shimmer 1.5s infinite;
+ }
+}
+
+.skeleton-text {
+ height: 16px;
+ width: 80%;
+ background: rgba(255, 255, 255, 0.3);
+ border-radius: 4px;
+}
+
+@keyframes shimmer {
+ 100% {
+ transform: translateX(100%);
+ }
+}
diff --git a/src/samples/mediaco/CustomListView/CarouselListView.tsx b/src/samples/mediaco/CustomListView/CarouselListView.tsx
new file mode 100644
index 00000000..d4f337d3
--- /dev/null
+++ b/src/samples/mediaco/CustomListView/CarouselListView.tsx
@@ -0,0 +1,201 @@
+import React, { useEffect, useRef, useState, useCallback } from 'react';
+import Card from '@mui/material/Card';
+import './CarouselListView.scss';
+
+interface CarouselProps {
+ data: any[];
+ getPConnect?: any;
+ title?: string;
+ showAllItems?: () => void;
+ referenceDataPage?: string;
+}
+
+export const Carousel: React.FC = ({ data, title, showAllItems }) => {
+ const [isLoading, setIsLoading] = useState(true);
+ const [displayItems, setDisplayItems] = useState([]);
+
+ const scrollContainerRef = useRef(null);
+ const cardRefs = useRef<(HTMLDivElement | null)[]>([]);
+
+ const skeletonItems = new Array(6).fill(0);
+
+ // 1. Build Data and Preload Images
+ useEffect(() => {
+ if (!data || data.length === 0) {
+ setIsLoading(true);
+ return;
+ }
+
+ const mappedData = data.map(item => ({
+ title: item.Carouselheading || item.Description || 'Untitled',
+ img: item.ImageURL,
+ ...item
+ }));
+
+ let loopList = [...mappedData];
+ const MIN_ITEMS = 12;
+
+ if (loopList.length > 0) {
+ while (loopList.length < MIN_ITEMS) {
+ loopList = [...loopList, ...loopList];
+ }
+ }
+
+ const finalDisplayItems = [...loopList, ...loopList, ...loopList];
+ setDisplayItems(finalDisplayItems);
+
+ // Preload Logic
+ const uniqueUrls = [...new Set(finalDisplayItems.map(item => item.img))].filter(Boolean);
+ let loadedCount = 0;
+ const total = uniqueUrls.length;
+
+ if (total === 0) {
+ setIsLoading(false);
+ return;
+ }
+
+ uniqueUrls.forEach(url => {
+ const img = new Image();
+ img.src = url;
+
+ const onImageComplete = () => {
+ loadedCount++;
+ if (loadedCount === total) {
+ setIsLoading(false);
+ }
+ };
+
+ img.onload = onImageComplete;
+ img.onerror = onImageComplete;
+ });
+ }, [data]);
+
+ // The Scroll Math Logic
+ const handleScroll = useCallback(
+ (event: Event) => {
+ if (isLoading) return;
+
+ const container = event.target as HTMLElement;
+ if (!container) return;
+
+ requestAnimationFrame(() => {
+ const totalWidth = container.scrollWidth;
+ const singleSetWidth = totalWidth / 3;
+ const currentScroll = container.scrollLeft;
+
+ // Infinite scroll snapping
+ if (currentScroll < 100) {
+ container.scrollLeft = currentScroll + singleSetWidth;
+ } else if (currentScroll >= singleSetWidth * 2 - 100) {
+ container.scrollLeft = currentScroll - singleSetWidth;
+ }
+
+ const containerRect = container.getBoundingClientRect();
+ if (containerRect.width === 0) return;
+
+ // Dynamic width and opacity calculation
+ cardRefs.current.forEach(el => {
+ if (!el) return;
+ const rect = el.getBoundingClientRect();
+ const cardCenter = rect.left - containerRect.left + rect.width / 2;
+ const containerCenter = containerRect.width / 2;
+ const distance = Math.abs(containerCenter - cardCenter);
+
+ const activeZone = 400;
+ const minWidth = 200;
+ const maxWidth = 500;
+ let currentWidth = minWidth;
+ let opacity = 0.7;
+
+ if (distance < activeZone) {
+ const factor = 1 - distance / activeZone;
+ currentWidth = minWidth + (maxWidth - minWidth) * factor;
+ opacity = 0.7 + 0.3 * factor;
+ }
+
+ el.style.flexBasis = `${currentWidth}px`;
+ el.style.minWidth = `${currentWidth}px`;
+ el.style.opacity = `${opacity}`;
+ });
+ });
+ },
+ [isLoading]
+ );
+
+ // Attach Scroll Listener
+ useEffect(() => {
+ const container = scrollContainerRef.current;
+ if (container) {
+ container.addEventListener('scroll', handleScroll);
+ return () => container.removeEventListener('scroll', handleScroll);
+ }
+ }, [handleScroll]);
+
+ // Initialize Scroll Position once loading finishes
+ useEffect(() => {
+ if (!isLoading && scrollContainerRef.current) {
+ const container = scrollContainerRef.current;
+
+ // setTimeout allows the browser to paint the flex items before measuring
+ setTimeout(() => {
+ if (container.scrollWidth > 0) {
+ const singleSetWidth = container.scrollWidth / 3;
+ container.scrollLeft = singleSetWidth;
+
+ // Trigger the calculation to set initial card sizes
+ handleScroll({ target: container } as any);
+ }
+ }, 0);
+ }
+ }, [isLoading, handleScroll]);
+
+ return (
+
+ {title && (
+
+
{title}
+
+ )}
+
+
+
+ {isLoading
+ ? // Skeleton State
+ skeletonItems.map((_, index) => (
+
+ ))
+ : // Loaded State
+ displayItems.map((item, index) => (
+
{
+ cardRefs.current[index] = el;
+ }}
+ >
+
+
+
+
{item.title}
+
+
+
+ ))}
+
+
+
+ {showAllItems && (
+
+ Show all
+
+ )}
+
+ );
+};
diff --git a/src/samples/mediaco/CustomListView/ListViewWrapper.tsx b/src/samples/mediaco/CustomListView/ListViewWrapper.tsx
new file mode 100644
index 00000000..10212d79
--- /dev/null
+++ b/src/samples/mediaco/CustomListView/ListViewWrapper.tsx
@@ -0,0 +1,158 @@
+import { useEffect, useMemo, useRef, useState } from 'react';
+import ActivityListView from './ActivityListView';
+import './ActivityListView.scss';
+import { Carousel } from './CarouselListView';
+
+import { modifyListData } from './utils';
+import type { PConnProps } from '@pega/react-sdk-components/lib/types/PConnProps';
+
+interface ListViewProps extends PConnProps {
+ bInForm?: boolean;
+ globalSearch?: boolean;
+ referenceList?: any[];
+ selectionMode?: string;
+ referenceType?: string;
+ payload?: any;
+ parameters?: any;
+ compositeKeys?: any;
+ showDynamicFields?: boolean;
+ readonlyContextList?: any;
+ value: any;
+ viewName?: string;
+ showRecords?: boolean;
+ displayAs?: string;
+}
+
+export default function ListViewWrapper(props: ListViewProps) {
+ const [items, setItems] = useState([]);
+ const [open, setOpen] = useState(false);
+ const closeBtnRef = useRef(null);
+ const closeModal = () => setOpen(false);
+ const { getPConnect } = props;
+ const thePConn = getPConnect();
+ const configProps: any = thePConn.getConfigProps() as ListViewProps;
+ const referenceDataPage = configProps.referenceList;
+ const template = configProps.presets[0]?.template;
+ const itemCountLabel = useMemo(() => {
+ const n = items?.length ?? 0;
+ return `${n} item${n === 1 ? '' : 's'} available`;
+ }, [items]);
+
+ useEffect(() => {
+ PCore.getDataPageUtils()
+ .getDataAsync(referenceDataPage, thePConn.getContextName())
+ .then(({ data }) => {
+ const modifiedData = data ? modifyListData(data, referenceDataPage) : [];
+ setItems(modifiedData);
+ });
+ }, []);
+
+ // ESC close + scroll lock + focus close button
+ useEffect(() => {
+ if (!open) return;
+
+ const onKeyDown = e => {
+ if (e.key === 'Escape') closeModal();
+ };
+
+ document.addEventListener('keydown', onKeyDown);
+ const prevOverflow = document.body.style.overflow;
+ document.body.style.overflow = 'hidden';
+ return () => {
+ document.removeEventListener('keydown', onKeyDown);
+ document.body.style.overflow = prevOverflow;
+ };
+ }, [open]);
+
+ function showAllItems() {
+ setOpen(true);
+ }
+
+ const title =
+ referenceDataPage === 'D_AccountHistoryList'
+ ? 'Activity'
+ : referenceDataPage === 'D_TrendingItemsList'
+ ? 'Trending Now'
+ : referenceDataPage === 'D_CarouselitemList'
+ ? 'Featured Content'
+ : '';
+
+ return (
+
+ {template === 'Table' &&
}
+ {template === 'Gallery' &&
}
+ {open && (
+
+
+
+
+
{title === 'Activity' ? 'All Activities' : title}
+
{itemCountLabel}
+
+
+
+
+
+
+
+
+ {template === 'Table' ? (
+ // Existing List View Render
+
+ {items.map((item, idx) => (
+
+
+

+
+
+
{item.title}
+
{item.description}
+
+
+ ))}
+
+ ) : template === 'Gallery' ? (
+
+ {items.map((item, idx) => (
+
+

+
{item.Carouselheading}
+
+ ))}
+
+ ) : null}
+
+
+
+ )}
+
+ );
+}
+
+function XIcon() {
+ return (
+
+ );
+}
+
+function Modal({ children, onClose }) {
+ return (
+
+ {/* Backdrop */}
+
+ {/* Panel */}
+
{children}
+
+ );
+}
diff --git a/src/samples/mediaco/CustomListView/utils.ts b/src/samples/mediaco/CustomListView/utils.ts
new file mode 100644
index 00000000..800f0726
--- /dev/null
+++ b/src/samples/mediaco/CustomListView/utils.ts
@@ -0,0 +1,114 @@
+import { SdkConfigAccess } from '@pega/auth/lib/sdk-auth-manager';
+
+export function getImageSrc(name, serverUrl) {
+ let iconName = name.replace('pi-', '').replace('pi ', '').trim();
+ if (iconName === 'line-chart') {
+ iconName = 'chart-line';
+ }
+
+ return getIconPath(serverUrl).concat(iconName).concat('.svg');
+}
+
+export function getIconPath(serverUrl) {
+ return serverUrl.concat('icons/');
+}
+
+export function getSDKStaticConentUrl() {
+ const sdkConfigServer = SdkConfigAccess.getSdkConfigServer();
+
+ if (!sdkConfigServer.sdkContentServerUrl.endsWith('/')) {
+ sdkConfigServer.sdkContentServerUrl = `${sdkConfigServer.sdkContentServerUrl}/`;
+ }
+
+ return `${sdkConfigServer.sdkContentServerUrl}constellation/`;
+}
+
+export function getIcon(activityType) {
+ switch (activityType) {
+ case 'Upgrade plan':
+ return 'trending-up';
+ case 'Make payment':
+ return 'wallet';
+ case 'Add Streaming':
+ return 'monitor';
+ case 'New Service':
+ return 'shopping-cart';
+ case 'Get Help':
+ return 'headphones';
+ case 'Purchase Phone':
+ return 'smartphone';
+ default:
+ return '';
+ }
+}
+
+export function timeSince(date: Date): string {
+ const seconds = Math.floor((new Date().getTime() - date.getTime()) / 1000);
+ let interval = seconds / 31536000;
+ if (interval > 1) {
+ return Math.floor(interval) + 'y ago';
+ }
+ interval = seconds / 2592000;
+ if (interval > 1) {
+ return Math.floor(interval) + ' months ago';
+ }
+ interval = seconds / 86400;
+ if (interval > 1) {
+ return Math.floor(interval) + 'd ago';
+ }
+ interval = seconds / 3600;
+ if (interval > 1) {
+ return Math.floor(interval) + 'h ago';
+ }
+ interval = seconds / 60;
+ if (interval > 1) {
+ return Math.floor(interval) + 'm ago';
+ }
+ return Math.floor(seconds) + 's ago';
+}
+
+export function modifyListData(data: any[], referenceDataPage: string) {
+ if (referenceDataPage === 'D_AccountHistoryList') {
+ const caseTypeToActivityMap = {
+ 'Plan Upgrade': 'Upgrade plan',
+ Payment: 'Make payment',
+ AddOnPurchase: 'New Service',
+ 'New Statement': 'New Service',
+ ProfileUpdated: 'Get Help',
+ 'Product Demo': 'Purchase Phone',
+ 'Client Onboarding': 'Add Streaming',
+ 'Quarterly Review': 'New Service',
+ 'Sales Meeting': 'New Service'
+ };
+ return data.map(item => {
+ const caseType = caseTypeToActivityMap[item.ActivityType];
+ return {
+ id: item.pyGUID,
+ iconSrc: getImageSrc(getIcon(caseType), getSDKStaticConentUrl()),
+ title: item.ActivityType,
+ time: timeSince(new Date(item.pxUpdateDateTime || item.pxCreateDateTime)),
+ description: item.Description
+ };
+ });
+ } else if (referenceDataPage === 'D_TrendingItemsList') {
+ return data.map(item => {
+ return {
+ id: item.ID,
+ title: item.Name,
+ time: null,
+ description: item.Genere,
+ views: `${item.Views} views`,
+ rating: item.Rating
+ };
+ });
+ } else if (referenceDataPage === 'D_CarouselitemList') {
+ return data.map((item, index) => {
+ return {
+ id: item.pyGUID || `carousel-item-${index}`,
+ Carouselheading: item.Carouselheading,
+ ImageURL: item.ImageURL
+ };
+ });
+ }
+ return [];
+}
diff --git a/src/samples/mediaco/sdk-mediaco-component-map.ts b/src/samples/mediaco/sdk-mediaco-component-map.ts
index 35bd3a88..906c8587 100644
--- a/src/samples/mediaco/sdk-mediaco-component-map.ts
+++ b/src/samples/mediaco/sdk-mediaco-component-map.ts
@@ -1,11 +1,14 @@
// Statically load all "MediaCo" components.
+import ListViewWrapper from './CustomListView/ListViewWrapper';
+
/* import end - DO NOT REMOVE */
// sdkMediaCoComponentMap is the JSON object where we'll store the components that are
// specific to MediaCo application.
const sdkMediaCoComponentMap = {
+ ListView: ListViewWrapper
/* map end - DO NOT REMOVE */
};