From e25c571c8d7a684507b2511c5ac7df88038be33d Mon Sep 17 00:00:00 2001
From: Tushar <72930233+tusharmane-tm@users.noreply.github.com>
Date: Mon, 9 Feb 2026 14:55:33 +0530
Subject: [PATCH 1/4] feat: Add session list block with JSON data fetching,
search, filtering, and pagination
---
blocks/session-list/session-list.css | 206 ++++++++++++--
blocks/session-list/session-list.js | 401 ++++++++++++++++++++++-----
blocks/speakers/speakers.css | 86 ------
blocks/speakers/speakers.js | 220 ---------------
ue/models/_section.json | 1 -
ue/models/blocks/_speaker.json | 33 ---
6 files changed, 505 insertions(+), 442 deletions(-)
delete mode 100644 blocks/speakers/speakers.css
delete mode 100644 blocks/speakers/speakers.js
delete mode 100644 ue/models/blocks/_speaker.json
diff --git a/blocks/session-list/session-list.css b/blocks/session-list/session-list.css
index 43911a6..b269517 100644
--- a/blocks/session-list/session-list.css
+++ b/blocks/session-list/session-list.css
@@ -1,52 +1,154 @@
.session-list-container {
- margin: 20px 0;
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+.session-search-container {
+ margin-bottom: 20px;
+ display: flex;
+ justify-content: center;
+}
+
+.session-search-input {
+ width: 100%;
+ max-width: 600px;
+ padding: 12px;
+ border: 1px solid var(--light-color);
+ border-radius: 4px;
+ font-size: 1rem;
+ box-sizing: border-box;
+}
+
+.session-filter-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 15px;
+ margin-bottom: 20px;
+ justify-content: center;
+ align-items: center;
+}
+
+.session-category-filter,
+.session-sort-select {
+ padding: 10px;
+ border: 1px solid var(--light-color);
+ border-radius: 4px;
+ font-size: 1rem;
+ background-color: var(--background-color);
+ color: var(--text-color);
+}
+
+.session-pagination-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 15px;
+ margin-bottom: 30px;
+ flex-wrap: wrap;
+}
+
+.session-pagination-btn {
+ padding: 10px 20px;
+ border: 1px solid var(--light-color);
+ border-radius: 4px;
+ background-color: var(--background-color);
+ color: var(--text-color);
+ cursor: pointer;
+ font-size: 1rem;
+ transition: all 0.3s ease;
+}
+
+.session-pagination-btn:hover:not(:disabled) {
+ background-color: var(--light-color);
+}
+
+.session-pagination-btn:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.session-page-display {
+ font-size: 1rem;
+ color: var(--text-color);
}
-.session-item {
+.session-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
+ gap: 20px;
+ margin-bottom: 30px;
+}
+
+.session-card {
border: 1px solid var(--light-color);
border-radius: 8px;
padding: 20px;
- margin-bottom: 15px;
background-color: var(--background-color);
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
}
-.session-title {
- font-size: 1.3rem;
+.session-card:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 4px 8px rgba(0,0,0,0.15);
+}
+
+.session-card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 15px;
+}
+
+.session-card-title {
+ font-size: 1.2rem;
font-weight: bold;
+ margin: 0;
+ color: var(--text-color);
+}
+
+.session-card-category {
+ background-color: var(--light-blue-color);
+ color: var(--background-color);
+ padding: 4px 8px;
+ border-radius: 4px;
+ font-size: 0.8rem;
+ font-weight: bold;
+ white-space: nowrap;
+}
+
+.session-card-speaker {
margin-bottom: 10px;
+ color: var(--link-color);
}
-.session-datetime {
+.session-card-date-time {
display: flex;
- flex-wrap: wrap;
gap: 10px;
- margin-bottom: 10px;
- font-weight: bold;
+ margin-bottom: 15px;
+ flex-wrap: wrap;
}
-.session-date,
-.session-time {
+.session-card-date,
+.session-card-time {
background-color: var(--light-color);
padding: 5px 10px;
border-radius: 4px;
font-size: 0.9rem;
}
-.session-speaker {
- font-style: italic;
- margin-bottom: 10px;
- color: var(--link-color);
-}
-
-.session-description {
- margin-bottom: 10px;
+.session-card-description {
+ margin-bottom: 15px;
line-height: 1.5;
+ color: var(--text-color);
}
-.session-tags {
+.session-card-tags {
display: flex;
flex-wrap: wrap;
gap: 5px;
+ margin-bottom: 15px;
}
.session-tag {
@@ -57,17 +159,67 @@
font-size: 0.8rem;
}
-@media (width >= 900px) {
+.session-card-link {
+ text-align: center;
+}
+
+.session-details-link {
+ display: inline-block;
+ padding: 8px 16px;
+ border: 1px solid var(--link-color);
+ border-radius: 4px;
+ background-color: transparent;
+ color: var(--link-color);
+ text-decoration: none;
+ font-weight: bold;
+ transition: all 0.3s ease;
+}
+
+.session-details-link:hover {
+ background-color: var(--link-color);
+ color: var(--background-color);
+}
+
+.session-error {
+ text-align: center;
+ padding: 20px;
+ color: var(--error-color);
+ font-weight: bold;
+}
+
+/* Responsive design */
+@media (width < 768px) {
.session-list-container {
- margin: 30px 0;
+ padding: 10px;
+ }
+
+ .session-search-input {
+ max-width: 100%;
+ }
+
+ .session-filter-container {
+ flex-direction: column;
+ align-items: stretch;
}
- .session-item {
- padding: 25px;
- margin-bottom: 20px;
+ .session-pagination-container {
+ flex-direction: column;
+ gap: 10px;
}
- .session-title {
- font-size: 1.5rem;
+ .session-grid {
+ grid-template-columns: 1fr;
+ gap: 15px;
+ }
+
+ .session-card-header {
+ flex-direction: column;
+ gap: 10px;
+ }
+}
+
+@media (width >= 768px) and (width < 1024px) {
+ .session-grid {
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
}
\ No newline at end of file
diff --git a/blocks/session-list/session-list.js b/blocks/session-list/session-list.js
index f915f98..637efb4 100644
--- a/blocks/session-list/session-list.js
+++ b/blocks/session-list/session-list.js
@@ -1,102 +1,353 @@
/*
* Session List Block
- * Display list of sessions with key details
+ * Display list of sessions with key details from JSON data
*/
export default function decorate(block) {
+ // Get the JSON data endpoint from the block's first data attribute
+ const jsonEndpoint = block.dataset.jsonEndpoint || null;
+
// Create session list container
const sessionListContainer = document.createElement('div');
sessionListContainer.className = 'session-list-container';
- // Process each row as a session
- const rows = [...block.children];
+ // Create search bar
+ const searchContainer = document.createElement('div');
+ searchContainer.className = 'session-search-container';
- rows.forEach((row, index) => {
- // Skip if it's the header row
- if (index === 0) {
- row.classList.add('session-list-header');
- return;
- }
+ const searchInput = document.createElement('input');
+ searchInput.type = 'text';
+ searchInput.placeholder = 'Search sessions by title, speaker, or description...';
+ searchInput.className = 'session-search-input';
- // Create session item
- const sessionItem = document.createElement('div');
- sessionItem.className = 'session-item';
-
- const cells = [...row.children];
-
- // Process cells: title, date, time, speaker, description, tags
- if (cells.length >= 5) {
- const titleCell = cells[0];
- const dateCell = cells[1];
- const timeCell = cells[2];
- const speakerCell = cells[3];
- const descriptionCell = cells[4];
- const tagsCell = cells[5] || null;
-
- // Add title
- if (titleCell.textContent.trim()) {
- const titleDiv = document.createElement('div');
- titleDiv.className = 'session-title';
- titleDiv.textContent = titleCell.textContent.trim();
- sessionItem.appendChild(titleDiv);
- }
+ searchContainer.appendChild(searchInput);
+ sessionListContainer.appendChild(searchContainer);
- // Add date and time
- const dateTimeDiv = document.createElement('div');
- dateTimeDiv.className = 'session-datetime';
+ // Create filter controls
+ const filterContainer = document.createElement('div');
+ filterContainer.className = 'session-filter-container';
- if (dateCell.textContent.trim()) {
- const dateSpan = document.createElement('span');
- dateSpan.className = 'session-date';
- dateSpan.textContent = dateCell.textContent.trim();
- dateTimeDiv.appendChild(dateSpan);
- }
+ const categoryFilter = document.createElement('select');
+ categoryFilter.className = 'session-category-filter';
+ categoryFilter.innerHTML = `
+
+
+
+
+
+ `;
- if (timeCell.textContent.trim()) {
- const timeSpan = document.createElement('span');
- timeSpan.className = 'session-time';
- timeSpan.textContent = timeCell.textContent.trim();
- dateTimeDiv.appendChild(timeSpan);
- }
+ const sortBySelect = document.createElement('select');
+ sortBySelect.className = 'session-sort-select';
+ sortBySelect.innerHTML = `
+
+
+
+ `;
+
+ filterContainer.appendChild(categoryFilter);
+ filterContainer.appendChild(sortBySelect);
+ sessionListContainer.appendChild(filterContainer);
+
+ // Create pagination controls
+ const paginationContainer = document.createElement('div');
+ paginationContainer.className = 'session-pagination-container';
+
+ const prevButton = document.createElement('button');
+ prevButton.className = 'session-pagination-btn session-prev-btn';
+ prevButton.textContent = 'Previous';
+ prevButton.disabled = true;
- sessionItem.appendChild(dateTimeDiv);
+ const pageDisplay = document.createElement('span');
+ pageDisplay.className = 'session-page-display';
+ pageDisplay.textContent = 'Page 1 of 1';
- // Add speaker
- if (speakerCell.textContent.trim()) {
- const speakerDiv = document.createElement('div');
- speakerDiv.className = 'session-speaker';
- speakerDiv.textContent = speakerCell.textContent.trim();
- sessionItem.appendChild(speakerDiv);
+ const nextButton = document.createElement('button');
+ nextButton.className = 'session-pagination-btn session-next-btn';
+ nextButton.textContent = 'Next';
+ nextButton.disabled = true;
+
+ paginationContainer.appendChild(prevButton);
+ paginationContainer.appendChild(pageDisplay);
+ paginationContainer.appendChild(nextButton);
+ sessionListContainer.appendChild(paginationContainer);
+
+ // Create session grid
+ const sessionGrid = document.createElement('div');
+ sessionGrid.className = 'session-grid';
+ sessionListContainer.appendChild(sessionGrid);
+
+ // Store session data
+ let allSessions = [];
+ let filteredSessions = [];
+ let currentPage = 1;
+ const itemsPerPage = 10;
+
+ // Function to fetch and process JSON data
+ async function fetchSessions() {
+ try {
+ if (!jsonEndpoint) {
+ // If no endpoint provided, create sample data
+ allSessions = [
+ {
+ id: 1,
+ title: "Introduction to Modern Web Development",
+ speaker: "Jane Smith",
+ description: "Learn the latest trends and best practices in web development.",
+ date: "2023-10-15",
+ time: "09:00 - 10:30",
+ category: "talk",
+ tags: ["web", "development", "javascript"]
+ },
+ {
+ id: 2,
+ title: "Advanced CSS Techniques",
+ speaker: "John Doe",
+ description: "Deep dive into modern CSS features and layout techniques.",
+ date: "2023-10-15",
+ time: "11:00 - 12:30",
+ category: "workshop",
+ tags: ["css", "frontend", "design"]
+ },
+ {
+ id: 3,
+ title: "Building Scalable Applications",
+ speaker: "Alice Johnson",
+ description: "How to design and build scalable applications using modern frameworks.",
+ date: "2023-10-15",
+ time: "14:00 - 15:30",
+ category: "keynote",
+ tags: ["architecture", "scalability", "frameworks"]
+ },
+ {
+ id: 4,
+ title: "JavaScript Performance Optimization",
+ speaker: "Bob Wilson",
+ description: "Techniques to optimize JavaScript performance for better user experience.",
+ date: "2023-10-16",
+ time: "10:00 - 11:30",
+ category: "talk",
+ tags: ["javascript", "performance", "optimization"]
+ },
+ {
+ id: 5,
+ title: "Responsive Design Patterns",
+ speaker: "Sarah Davis",
+ description: "Best practices for creating responsive designs that work across all devices.",
+ date: "2023-10-16",
+ time: "13:00 - 14:30",
+ category: "workshop",
+ tags: ["responsive", "design", "mobile"]
+ },
+ {
+ id: 6,
+ title: "API Security Best Practices",
+ speaker: "Michael Brown",
+ description: "Essential security practices for API development and deployment.",
+ date: "2023-10-16",
+ time: "15:00 - 16:30",
+ category: "panel",
+ tags: ["security", "api", "development"]
+ },
+ {
+ id: 7,
+ title: "The Future of Web Technologies",
+ speaker: "Emma Taylor",
+ description: "Exploring upcoming web technologies and their potential impact.",
+ date: "2023-10-17",
+ time: "09:30 - 11:00",
+ category: "keynote",
+ tags: ["future", "technology", "innovation"]
+ },
+ {
+ id: 8,
+ title: "Accessibility in Modern Web Apps",
+ speaker: "David Miller",
+ description: "How to make web applications accessible to all users.",
+ date: "2023-10-17",
+ time: "12:00 - 13:30",
+ category: "talk",
+ tags: ["accessibility", "wcag", "inclusive"]
+ },
+ {
+ id: 9,
+ title: "State Management in React Applications",
+ speaker: "Lisa Anderson",
+ description: "Advanced patterns for state management in React applications.",
+ date: "2023-10-17",
+ time: "14:00 - 15:30",
+ category: "workshop",
+ tags: ["react", "state", "redux"]
+ },
+ {
+ id: 10,
+ title: "Cloud-Native Development Patterns",
+ speaker: "Robert Thomas",
+ description: "Building applications for the cloud using modern patterns and practices.",
+ date: "2023-10-17",
+ time: "16:00 - 17:30",
+ category: "talk",
+ tags: ["cloud", "development", "patterns"]
+ },
+ {
+ id: 11,
+ title: "Machine Learning for Web Developers",
+ speaker: "Jennifer White",
+ description: "Introduction to machine learning concepts and how they apply to web development.",
+ date: "2023-10-18",
+ time: "10:00 - 11:30",
+ category: "workshop",
+ tags: ["ml", "ai", "web"]
+ },
+ {
+ id: 12,
+ title: "DevOps Practices for Modern Teams",
+ speaker: "Christopher Lee",
+ description: "Effective DevOps practices to improve team productivity and deployment reliability.",
+ date: "2023-10-18",
+ time: "13:00 - 14:30",
+ category: "panel",
+ tags: ["devops", "ci/cd", "teams"]
+ }
+ ];
+ } else {
+ // Fetch data from the provided endpoint
+ const response = await fetch(jsonEndpoint);
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ const data = await response.json();
+ allSessions = Array.isArray(data) ? data : (data.sessions || []);
}
- // Add description
- if (descriptionCell.textContent.trim()) {
- const descDiv = document.createElement('div');
- descDiv.className = 'session-description';
- descDiv.textContent = descriptionCell.textContent.trim();
- sessionItem.appendChild(descDiv);
+ // Process the data
+ filteredSessions = [...allSessions];
+ renderSessions();
+ } catch (error) {
+ console.error('Error fetching sessions:', error);
+ // Create a fallback UI with error message
+ const errorDiv = document.createElement('div');
+ errorDiv.className = 'session-error';
+ errorDiv.textContent = 'Failed to load sessions. Please try again later.';
+ sessionListContainer.innerHTML = '';
+ sessionListContainer.appendChild(errorDiv);
+ }
+ }
+
+ // Function to filter sessions based on search term
+ function filterSessions() {
+ const searchTerm = searchInput.value.toLowerCase();
+ const categoryFilterValue = categoryFilter.value;
+
+ filteredSessions = allSessions.filter(session => {
+ const matchesSearch =
+ (session.title && session.title.toLowerCase().includes(searchTerm)) ||
+ (session.speaker && session.speaker.toLowerCase().includes(searchTerm)) ||
+ (session.description && session.description.toLowerCase().includes(searchTerm));
+
+ const matchesCategory = categoryFilterValue ? session.category === categoryFilterValue : true;
+
+ return matchesSearch && matchesCategory;
+ });
+
+ currentPage = 1;
+ renderSessions();
+ }
+
+ // Function to sort sessions
+ function sortSessions() {
+ const sortBy = sortBySelect.value;
+
+ filteredSessions.sort((a, b) => {
+ switch (sortBy) {
+ case 'date':
+ return new Date(a.date) - new Date(b.date);
+ case 'title':
+ return (a.title || '').localeCompare(b.title || '');
+ case 'speaker':
+ return (a.speaker || '').localeCompare(b.speaker || '');
+ default:
+ return 0;
}
+ });
+
+ renderSessions();
+ }
+
+ // Function to render sessions with pagination
+ function renderSessions() {
+ // Clear the grid
+ sessionGrid.innerHTML = '';
- // Add tags
- if (tagsCell && tagsCell.textContent.trim()) {
- const tagsDiv = document.createElement('div');
- tagsDiv.className = 'session-tags';
+ // Calculate pagination
+ const totalPages = Math.ceil(filteredSessions.length / itemsPerPage);
+ const startIndex = (currentPage - 1) * itemsPerPage;
+ const endIndex = startIndex + itemsPerPage;
+ const currentSessions = filteredSessions.slice(startIndex, endIndex);
- const tags = tagsCell.textContent.trim().split(',');
- tags.forEach((tag) => {
- const tagSpan = document.createElement('span');
- tagSpan.className = 'session-tag';
- tagSpan.textContent = tag.trim();
- tagsDiv.appendChild(tagSpan);
- });
+ // Update pagination controls
+ prevButton.disabled = currentPage === 1;
+ nextButton.disabled = currentPage === totalPages || totalPages === 0;
+ pageDisplay.textContent = `Page ${currentPage} of ${totalPages || 1}`;
- sessionItem.appendChild(tagsDiv);
+ // Render session cards
+ currentSessions.forEach(session => {
+ const sessionCard = document.createElement('div');
+ sessionCard.className = 'session-card';
+
+ // Create session card content
+ sessionCard.innerHTML = `
+
+
+ Speaker: ${session.speaker || 'TBD'}
+
+
+ ${session.date || 'Date TBD'}
+ ${session.time || 'Time TBD'}
+
+
+ ${session.description || 'No description available.'}
+
+
+ ${session.tags && session.tags.length > 0
+ ? session.tags.map(tag => `${tag}`).join('')
+ : ''}
+
+
+ `;
+
+ sessionGrid.appendChild(sessionCard);
+ });
+
+ // Add event listeners for pagination
+ prevButton.addEventListener('click', () => {
+ if (currentPage > 1) {
+ currentPage--;
+ renderSessions();
}
- }
+ });
+
+ nextButton.addEventListener('click', () => {
+ if (currentPage < totalPages) {
+ currentPage++;
+ renderSessions();
+ }
+ });
+
+ // Add event listeners for filters and search
+ searchInput.addEventListener('input', filterSessions);
+ categoryFilter.addEventListener('change', filterSessions);
+ sortBySelect.addEventListener('change', sortSessions);
+ }
- sessionListContainer.appendChild(sessionItem);
- });
+ // Fetch and display sessions
+ fetchSessions();
// Replace the block with the session list
block.innerHTML = '';
block.appendChild(sessionListContainer);
-}
+}
\ No newline at end of file
diff --git a/blocks/speakers/speakers.css b/blocks/speakers/speakers.css
deleted file mode 100644
index 8b7910f..0000000
--- a/blocks/speakers/speakers.css
+++ /dev/null
@@ -1,86 +0,0 @@
-.speakers-container {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
- gap: 20px;
- margin: 20px 0;
-}
-
-.speaker-card {
- border: 1px solid var(--light-color);
- border-radius: 8px;
- padding: 20px;
- text-align: center;
- background-color: var(--background-color);
- transition: transform 0.3s ease, box-shadow 0.3s ease;
-}
-
-.speaker-card:hover {
- transform: translateY(-5px);
- box-shadow: 0 5px 15px rgb(0 0 0 / 10%);
-}
-
-.speaker-photo {
- margin-bottom: 15px;
-}
-
-.speaker-photo img {
- width: 120px;
- height: 120px;
- border-radius: 50%;
- object-fit: cover;
- border: 3px solid var(--light-color);
-}
-
-.speaker-name {
- font-size: 1.2rem;
- font-weight: bold;
- margin-bottom: 5px;
-}
-
-.speaker-title {
- color: var(--link-color);
- margin-bottom: 5px;
-}
-
-.speaker-company {
- font-size: 0.9rem;
- color: var(--dark-color);
- margin-bottom: 10px;
-}
-
-.speaker-bio {
- font-size: 0.9rem;
- line-height: 1.4;
- margin-bottom: 10px;
-}
-
-.speaker-social {
- margin-top: 10px;
-}
-
-.speaker-social-link {
- display: inline-block;
- margin: 0 5px;
- font-size: 0.8rem;
- color: var(--link-color);
-}
-
-@media (width >= 900px) {
- .speakers-container {
- grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
- gap: 25px;
- }
-
- .speaker-card {
- padding: 25px;
- }
-
- .speaker-photo img {
- width: 140px;
- height: 140px;
- }
-
- .speaker-name {
- font-size: 1.3rem;
- }
-}
\ No newline at end of file
diff --git a/blocks/speakers/speakers.js b/blocks/speakers/speakers.js
deleted file mode 100644
index a87ca21..0000000
--- a/blocks/speakers/speakers.js
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Speaker Cards Block
- * Display speaker profiles with photos, bios, and company info
- * Supports both table-based and Content Fragment-based content
- */
-
-// Function to create speaker card from fragment data
-function createSpeakerCardFromFragment(speakerData) {
- const speakerCard = document.createElement('div');
- speakerCard.className = 'speaker-card';
-
- // Add photo
- if (speakerData.image_ext) {
- const photoDiv = document.createElement('div');
- photoDiv.className = 'speaker-photo';
-
- const img = document.createElement('img');
- img.src = speakerData.image_ext;
- img.alt = speakerData.title || 'Speaker';
- photoDiv.appendChild(img);
- speakerCard.appendChild(photoDiv);
- } else if (speakerData.image) {
- // Fallback to image property if image_ext is not available
- const photoDiv = document.createElement('div');
- photoDiv.className = 'speaker-photo';
-
- const img = document.createElement('img');
- img.src = speakerData.image;
- img.alt = speakerData.title || 'Speaker';
- photoDiv.appendChild(img);
- speakerCard.appendChild(photoDiv);
- }
-
- // Add name
- if (speakerData.title) {
- const nameDiv = document.createElement('div');
- nameDiv.className = 'speaker-name';
- nameDiv.textContent = speakerData.title;
- speakerCard.appendChild(nameDiv);
- }
-
- // Add title
- if (speakerData.jobTitle) {
- const titleDiv = document.createElement('div');
- titleDiv.className = 'speaker-title';
- titleDiv.textContent = speakerData.jobTitle;
- speakerCard.appendChild(titleDiv);
- }
-
- // Add company
- if (speakerData.companyName) {
- const companyDiv = document.createElement('div');
- companyDiv.className = 'speaker-company';
- companyDiv.textContent = speakerData.companyName;
- speakerCard.appendChild(companyDiv);
- }
-
- // Add bio (from description)
- if (speakerData.description && speakerData.description.html) {
- const bioDiv = document.createElement('div');
- bioDiv.className = 'speaker-bio';
- bioDiv.textContent = speakerData.description.html;
- speakerCard.appendChild(bioDiv);
- }
-
- // Add social links (if available)
- if (speakerData.tags && speakerData.tags.length > 0) {
- const socialDiv = document.createElement('div');
- socialDiv.className = 'speaker-social';
-
- speakerData.tags.forEach((tag) => {
- const linkSpan = document.createElement('span');
- linkSpan.className = 'speaker-social-link';
- linkSpan.textContent = tag;
- socialDiv.appendChild(linkSpan);
- });
-
- speakerCard.appendChild(socialDiv);
- }
-
- return speakerCard;
-}
-
-// Function to process table-based speakers (used in both modes)
-function processTableBasedSpeakers(block, container) {
- // Process each row as a speaker
- const rows = [...block.children];
-
- rows.forEach((row, index) => {
- // Skip if it's the header row
- if (index === 0) {
- row.classList.add('speakers-header');
- return;
- }
-
- // Create speaker card
- const speakerCard = document.createElement('div');
- speakerCard.className = 'speaker-card';
-
- const cells = [...row.children];
-
- // Process cells: name, title, company, photo, bio, social links
- if (cells.length >= 4) {
- const nameCell = cells[0];
- const titleCell = cells[1];
- const companyCell = cells[2];
- const photoCell = cells[3];
- const bioCell = cells[4] || null;
- const socialCell = cells[5] || null;
-
- // Add photo
- if (photoCell.textContent.trim()) {
- const photoDiv = document.createElement('div');
- photoDiv.className = 'speaker-photo';
-
- // If it's an image, use it directly
- if (photoCell.querySelector('img')) {
- photoDiv.appendChild(photoCell.firstElementChild);
- } else {
- // Otherwise, treat as a URL
- const img = document.createElement('img');
- img.src = photoCell.textContent.trim();
- img.alt = nameCell.textContent.trim();
- photoDiv.appendChild(img);
- }
-
- speakerCard.appendChild(photoDiv);
- }
-
- // Add name
- if (nameCell.textContent.trim()) {
- const nameDiv = document.createElement('div');
- nameDiv.className = 'speaker-name';
- nameDiv.textContent = nameCell.textContent.trim();
- speakerCard.appendChild(nameDiv);
- }
-
- // Add title
- if (titleCell.textContent.trim()) {
- const titleDiv = document.createElement('div');
- titleDiv.className = 'speaker-title';
- titleDiv.textContent = titleCell.textContent.trim();
- speakerCard.appendChild(titleDiv);
- }
-
- // Add company
- if (companyCell.textContent.trim()) {
- const companyDiv = document.createElement('div');
- companyDiv.className = 'speaker-company';
- companyDiv.textContent = companyCell.textContent.trim();
- speakerCard.appendChild(companyDiv);
- }
-
- // Add bio
- if (bioCell && bioCell.textContent.trim()) {
- const bioDiv = document.createElement('div');
- bioDiv.className = 'speaker-bio';
- bioDiv.textContent = bioCell.textContent.trim();
- speakerCard.appendChild(bioDiv);
- }
-
- // Add social links
- if (socialCell && socialCell.textContent.trim()) {
- const socialDiv = document.createElement('div');
- socialDiv.className = 'speaker-social';
-
- const socialLinks = socialCell.textContent.trim().split(',');
- socialLinks.forEach((link) => {
- const linkSpan = document.createElement('span');
- linkSpan.className = 'speaker-social-link';
- linkSpan.textContent = link.trim();
- socialDiv.appendChild(linkSpan);
- });
-
- speakerCard.appendChild(socialDiv);
- }
- }
-
- container.appendChild(speakerCard);
- });
-}
-
-// Function to load speakers from Content Fragment
-async function loadSpeakersFromFragment(fragmentPath, container) {
- try {
- const response = await fetch(fragmentPath);
- const data = await response.json();
-
- if (data && data.items) {
- data.items.forEach((item) => {
- const speakerCard = createSpeakerCardFromFragment(item);
- container.appendChild(speakerCard);
- });
- }
- } catch (error) {
- // Fallback to default behavior
- processTableBasedSpeakers(null, container);
- }
-}
-
-export default function decorate(block) {
- // Create speakers container
- const speakersContainer = document.createElement('div');
- speakersContainer.className = 'speakers-container';
-
- // Check if this is a fragment-based block
- const { fragmentPath } = block.dataset;
-
- if (fragmentPath) {
- // Load content from fragment
- loadSpeakersFromFragment(fragmentPath, speakersContainer);
- } else {
- // Fallback to table-based content
- processTableBasedSpeakers(block, speakersContainer);
- }
-
- // Replace the block with the speakers container
- block.innerHTML = '';
- block.appendChild(speakersContainer);
-}
diff --git a/ue/models/_section.json b/ue/models/_section.json
index 5bd434b..c42ed03 100644
--- a/ue/models/_section.json
+++ b/ue/models/_section.json
@@ -55,7 +55,6 @@
"image",
"quote",
"search",
- "speaker",
"tabs",
"table",
"text",
diff --git a/ue/models/blocks/_speaker.json b/ue/models/blocks/_speaker.json
deleted file mode 100644
index 4de8af0..0000000
--- a/ue/models/blocks/_speaker.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
- "definitions": [
- {
- "title": "Speaker",
- "id": "speaker",
- "plugins": {
- "xwalk": {
- "page": {
- "resourceType": "core/franklin/components/block/v1/block",
- "template": {
- "name": "Speaker",
- "model": "speaker"
- }
- }
- }
- }
- }
- ],
- "models": [
- {
- "id": "speaker",
- "fields": [
- {
- "component": "aem-content",
- "name": "fragmentPath",
- "label": "Content Fragment Path",
- "description": "Path to the Content Fragment containing speaker data"
- }
- ]
- }
- ],
- "filters": []
-}
\ No newline at end of file
From f32b0c0956ac2a3d4dd925a77b732ecd499d6dea Mon Sep 17 00:00:00 2001
From: Tushar <72930233+tusharmane-tm@users.noreply.github.com>
Date: Mon, 9 Feb 2026 15:07:22 +0530
Subject: [PATCH 2/4] feat: Add default JSON endpoint for session list block
and create model definition
---
blocks/session-list/session-list.js | 2 +-
ue/models/blocks/_session-list.json | 34 +++++++++++++++++++++++++++++
2 files changed, 35 insertions(+), 1 deletion(-)
create mode 100644 ue/models/blocks/_session-list.json
diff --git a/blocks/session-list/session-list.js b/blocks/session-list/session-list.js
index 637efb4..346e94c 100644
--- a/blocks/session-list/session-list.js
+++ b/blocks/session-list/session-list.js
@@ -4,7 +4,7 @@
*/
export default function decorate(block) {
// Get the JSON data endpoint from the block's first data attribute
- const jsonEndpoint = block.dataset.jsonEndpoint || null;
+ const jsonEndpoint = block.dataset.jsonEndpoint || 'https://raw.githubusercontent.com/initialyze/initialyzers-aem-data/refs/heads/main/session-catalog.json';
// Create session list container
const sessionListContainer = document.createElement('div');
diff --git a/ue/models/blocks/_session-list.json b/ue/models/blocks/_session-list.json
new file mode 100644
index 0000000..f96118f
--- /dev/null
+++ b/ue/models/blocks/_session-list.json
@@ -0,0 +1,34 @@
+{
+ "definitions": [
+ {
+ "title": "Session List",
+ "id": "session-list",
+ "plugins": {
+ "xwalk": {
+ "page": {
+ "resourceType": "core/franklin/components/block/v1/block",
+ "template": {
+ "name": "Session List",
+ "model": "session-list",
+ "filter": "session-list"
+ }
+ }
+ }
+ }
+ }
+ ],
+ "models": [
+ {
+ "id": "session-list",
+ "fields": [
+ {
+ "component": "text",
+ "name": "jsonEndpoint",
+ "label": "Session list Json Endpoint",
+ "description": "The JSON endpoint URL for session list data"
+ }
+ ]
+ }
+ ],
+ "filters": []
+}
From 1ec98ca0399af217629a4859b686f1a4712151de Mon Sep 17 00:00:00 2001
From: Tushar <72930233+tusharmane-tm@users.noreply.github.com>
Date: Mon, 9 Feb 2026 15:12:46 +0530
Subject: [PATCH 3/4] feat: Rename speaker component to session list and update
related models and filters
---
component-definition.json | 9 +++++----
component-filters.json | 2 +-
component-models.json | 10 +++++-----
skills/conference-website-implementation.md | 1 +
ue/models/_section.json | 1 +
5 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/component-definition.json b/component-definition.json
index ce6f0c0..60a867a 100644
--- a/component-definition.json
+++ b/component-definition.json
@@ -297,15 +297,16 @@
}
},
{
- "title": "Speaker",
- "id": "speaker",
+ "title": "Session List",
+ "id": "session-list",
"plugins": {
"xwalk": {
"page": {
"resourceType": "core/franklin/components/block/v1/block",
"template": {
- "name": "Speaker",
- "model": "speaker"
+ "name": "Session List",
+ "model": "session-list",
+ "filter": "session-list"
}
}
}
diff --git a/component-filters.json b/component-filters.json
index f17073d..a088c65 100644
--- a/component-filters.json
+++ b/component-filters.json
@@ -20,7 +20,7 @@
"image",
"quote",
"search",
- "speaker",
+ "session-list",
"tabs",
"table",
"text",
diff --git a/component-models.json b/component-models.json
index 8316f89..ba13542 100644
--- a/component-models.json
+++ b/component-models.json
@@ -365,13 +365,13 @@
]
},
{
- "id": "speaker",
+ "id": "session-list",
"fields": [
{
- "component": "aem-content",
- "name": "fragmentPath",
- "label": "Content Fragment Path",
- "description": "Path to the Content Fragment containing speaker data"
+ "component": "text",
+ "name": "jsonEndpoint",
+ "label": "Session list Json Endpoint",
+ "description": "The JSON endpoint URL for session list data"
}
]
},
diff --git a/skills/conference-website-implementation.md b/skills/conference-website-implementation.md
index af23d0b..2aeb512 100644
--- a/skills/conference-website-implementation.md
+++ b/skills/conference-website-implementation.md
@@ -34,6 +34,7 @@ This skill documents the complete implementation of a conference website solutio
### Model Structure
The speaker model uses a text field for `fragmentPath` which is the standard AEM pattern for referencing Content Fragments in Universal Editor, allowing content authors to select or enter the path to the Content Fragment that contains the speaker data.
+After making any changes in `ue/models/*` make sure to run `npm run build:json` to update components related json files.
### Usage Instructions
1. **Content Fragment Mode**: Add `data-fragment-path="/path/to/your/fragment.json"` attribute to the block
diff --git a/ue/models/_section.json b/ue/models/_section.json
index c42ed03..9685f8d 100644
--- a/ue/models/_section.json
+++ b/ue/models/_section.json
@@ -55,6 +55,7 @@
"image",
"quote",
"search",
+ "session-list",
"tabs",
"table",
"text",
From d8268f826474dd358bb339a1093640c77c64454b Mon Sep 17 00:00:00 2001
From: Tushar <72930233+tusharmane-tm@users.noreply.github.com>
Date: Mon, 9 Feb 2026 15:53:36 +0530
Subject: [PATCH 4/4] feat: Enhance session list functionality with pagination,
filtering, and improved search input
---
blocks/session-list/session-list.css | 12 +-
blocks/session-list/session-list.js | 394 +++++++++++++--------------
2 files changed, 203 insertions(+), 203 deletions(-)
diff --git a/blocks/session-list/session-list.css b/blocks/session-list/session-list.css
index b269517..c2fc0fb 100644
--- a/blocks/session-list/session-list.css
+++ b/blocks/session-list/session-list.css
@@ -59,15 +59,15 @@
transition: all 0.3s ease;
}
-.session-pagination-btn:hover:not(:disabled) {
- background-color: var(--light-color);
-}
-
.session-pagination-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
+.session-pagination-btn:hover:not(:disabled) {
+ background-color: var(--light-color);
+}
+
.session-page-display {
font-size: 1rem;
color: var(--text-color);
@@ -85,13 +85,13 @@
border-radius: 8px;
padding: 20px;
background-color: var(--background-color);
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+ box-shadow: 0 2px 4px rgb(0 0 0 / 10%);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.session-card:hover {
transform: translateY(-5px);
- box-shadow: 0 4px 8px rgba(0,0,0,0.15);
+ box-shadow: 0 4px 8px rgb(0 0 0 / 15%);
}
.session-card-header {
diff --git a/blocks/session-list/session-list.js b/blocks/session-list/session-list.js
index 346e94c..6135944 100644
--- a/blocks/session-list/session-list.js
+++ b/blocks/session-list/session-list.js
@@ -16,7 +16,7 @@ export default function decorate(block) {
const searchInput = document.createElement('input');
searchInput.type = 'text';
- searchInput.placeholder = 'Search sessions by title, speaker, or description...';
+ searchInput.placeholder = 'Search sessions by title, speaker, or description. ..';
searchInput.className = 'session-search-input';
searchContainer.appendChild(searchInput);
@@ -82,6 +82,111 @@ export default function decorate(block) {
let currentPage = 1;
const itemsPerPage = 10;
+ // Function to render sessions with pagination
+ function renderSessions() {
+ // Clear the grid
+ sessionGrid.innerHTML = '';
+
+ // Calculate pagination
+ const totalPages = Math.ceil(filteredSessions.length / itemsPerPage);
+ const startIndex = (currentPage - 1) * itemsPerPage;
+ const endIndex = startIndex + itemsPerPage;
+ const currentSessions = filteredSessions.slice(startIndex, endIndex);
+
+ // Update pagination controls
+ prevButton.disabled = currentPage === 1;
+ nextButton.disabled = currentPage === totalPages || totalPages === 0;
+ pageDisplay.textContent = `Page ${currentPage} of ${totalPages || 1}`;
+
+ // Render session cards
+ currentSessions.forEach((session) => {
+ const sessionCard = document.createElement('div');
+ sessionCard.className = 'session-card';
+
+ // Create session card content
+ sessionCard.innerHTML = `
+
+
+ Speaker: ${session.speaker || 'TBD'}
+
+
+ ${session.date || 'Date TBD'}
+ ${session.time || 'Time TBD'}
+
+
+ ${session.description || 'No description available.'}
+
+
+ ${session.tags && session.tags.length > 0
+ ? session.tags.map((tag) => `${tag}`).join('')
+ : ''}
+
+
+ `;
+
+ sessionGrid.appendChild(sessionCard);
+ });
+
+ // Add event listeners for pagination
+ prevButton.addEventListener('click', () => {
+ if (currentPage > 1) {
+ currentPage -= 1;
+ renderSessions();
+ }
+ });
+
+ nextButton.addEventListener('click', () => {
+ if (currentPage < totalPages) {
+ currentPage += 1;
+ renderSessions();
+ }
+ });
+ }
+
+ // Function to filter sessions based on search term
+ function filterSessions() {
+ const searchTerm = searchInput.value.toLowerCase();
+ const categoryFilterValue = categoryFilter.value;
+
+ filteredSessions = allSessions.filter((session) => {
+ const matchesSearch = (session.title && session.title.toLowerCase().includes(searchTerm))
+ || (session.speaker && session.speaker.toLowerCase().includes(searchTerm))
+ || (session.description && session.description.toLowerCase().includes(searchTerm));
+
+ const matchesCategory = categoryFilterValue ? session.category === categoryFilterValue : true;
+
+ return matchesSearch && matchesCategory;
+ });
+
+ currentPage = 1;
+ renderSessions();
+ }
+
+ // Function to sort sessions
+ function sortSessions() {
+ const sortBy = sortBySelect.value;
+
+ filteredSessions.sort((a, b) => {
+ switch (sortBy) {
+ case 'date':
+ return new Date(a.date) - new Date(b.date);
+ case 'title':
+ return (a.title || '').localeCompare(b.title || '');
+ case 'speaker':
+ return (a.speaker || '').localeCompare(b.speaker || '');
+ default:
+ return 0;
+ }
+ });
+
+ renderSessions();
+ }
+
// Function to fetch and process JSON data
async function fetchSessions() {
try {
@@ -90,124 +195,124 @@ export default function decorate(block) {
allSessions = [
{
id: 1,
- title: "Introduction to Modern Web Development",
- speaker: "Jane Smith",
- description: "Learn the latest trends and best practices in web development.",
- date: "2023-10-15",
- time: "09:00 - 10:30",
- category: "talk",
- tags: ["web", "development", "javascript"]
+ title: 'Introduction to Modern Web Development',
+ speaker: 'Jane Smith',
+ description: 'Learn the latest trends and best practices in web development.',
+ date: '2023-10-15',
+ time: '09:00 - 10:30',
+ category: 'talk',
+ tags: ['web', 'development', 'javascript'],
},
{
id: 2,
- title: "Advanced CSS Techniques",
- speaker: "John Doe",
- description: "Deep dive into modern CSS features and layout techniques.",
- date: "2023-10-15",
- time: "11:00 - 12:30",
- category: "workshop",
- tags: ["css", "frontend", "design"]
+ title: 'Advanced CSS Techniques',
+ speaker: 'John Doe',
+ description: 'Deep dive into modern CSS features and layout techniques.',
+ date: '2023-10-15',
+ time: '11:00 - 12:30',
+ category: 'workshop',
+ tags: ['css', 'frontend', 'design'],
},
{
id: 3,
- title: "Building Scalable Applications",
- speaker: "Alice Johnson",
- description: "How to design and build scalable applications using modern frameworks.",
- date: "2023-10-15",
- time: "14:00 - 15:30",
- category: "keynote",
- tags: ["architecture", "scalability", "frameworks"]
+ title: 'Building Scalable Applications',
+ speaker: 'Alice Johnson',
+ description: 'How to design and build scalable applications using modern frameworks.',
+ date: '2023-10-15',
+ time: '14:00 - 15:30',
+ category: 'keynote',
+ tags: ['architecture', 'scalability', 'frameworks'],
},
{
id: 4,
- title: "JavaScript Performance Optimization",
- speaker: "Bob Wilson",
- description: "Techniques to optimize JavaScript performance for better user experience.",
- date: "2023-10-16",
- time: "10:00 - 11:30",
- category: "talk",
- tags: ["javascript", "performance", "optimization"]
+ title: 'JavaScript Performance Optimization',
+ speaker: 'Bob Wilson',
+ description: 'Techniques to optimize JavaScript performance for better user experience.',
+ date: '2023-10-16',
+ time: '10:00 - 11:30',
+ category: 'talk',
+ tags: ['javascript', 'performance', 'optimization'],
},
{
id: 5,
- title: "Responsive Design Patterns",
- speaker: "Sarah Davis",
- description: "Best practices for creating responsive designs that work across all devices.",
- date: "2023-10-16",
- time: "13:00 - 14:30",
- category: "workshop",
- tags: ["responsive", "design", "mobile"]
+ title: 'Responsive Design Patterns',
+ speaker: 'Sarah Davis',
+ description: 'Best practices for creating responsive designs that work across all devices.',
+ date: '2023-10-16',
+ time: '13:00 - 14:30',
+ category: 'workshop',
+ tags: ['responsive', 'design', 'mobile'],
},
{
id: 6,
- title: "API Security Best Practices",
- speaker: "Michael Brown",
- description: "Essential security practices for API development and deployment.",
- date: "2023-10-16",
- time: "15:00 - 16:30",
- category: "panel",
- tags: ["security", "api", "development"]
+ title: 'API Security Best Practices',
+ speaker: 'Michael Brown',
+ description: 'Essential security practices for API development and deployment.',
+ date: '2023-10-16',
+ time: '15:00 - 16:30',
+ category: 'panel',
+ tags: ['security', 'api', 'development'],
},
{
id: 7,
- title: "The Future of Web Technologies",
- speaker: "Emma Taylor",
- description: "Exploring upcoming web technologies and their potential impact.",
- date: "2023-10-17",
- time: "09:30 - 11:00",
- category: "keynote",
- tags: ["future", "technology", "innovation"]
+ title: 'The Future of Web Technologies',
+ speaker: 'Emma Taylor',
+ description: 'Exploring upcoming web technologies and their potential impact.',
+ date: '2023-10-17',
+ time: '09:30 - 11:00',
+ category: 'keynote',
+ tags: ['future', 'technology', 'innovation'],
},
{
id: 8,
- title: "Accessibility in Modern Web Apps",
- speaker: "David Miller",
- description: "How to make web applications accessible to all users.",
- date: "2023-10-17",
- time: "12:00 - 13:30",
- category: "talk",
- tags: ["accessibility", "wcag", "inclusive"]
+ title: 'Accessibility in Modern Web Apps',
+ speaker: 'David Miller',
+ description: 'How to make web applications accessible to all users.',
+ date: '2023-10-17',
+ time: '12:00 - 13:30',
+ category: 'talk',
+ tags: ['accessibility', 'wcag', 'inclusive'],
},
{
id: 9,
- title: "State Management in React Applications",
- speaker: "Lisa Anderson",
- description: "Advanced patterns for state management in React applications.",
- date: "2023-10-17",
- time: "14:00 - 15:30",
- category: "workshop",
- tags: ["react", "state", "redux"]
+ title: 'State Management in React Applications',
+ speaker: 'Lisa Anderson',
+ description: 'Advanced patterns for state management in React applications.',
+ date: '2023-10-17',
+ time: '14:00 - 15:30',
+ category: 'workshop',
+ tags: ['react', 'state', 'redux'],
},
{
id: 10,
- title: "Cloud-Native Development Patterns",
- speaker: "Robert Thomas",
- description: "Building applications for the cloud using modern patterns and practices.",
- date: "2023-10-17",
- time: "16:00 - 17:30",
- category: "talk",
- tags: ["cloud", "development", "patterns"]
+ title: 'Cloud-Native Development Patterns',
+ speaker: 'Robert Thomas',
+ description: 'Building applications for the cloud using modern patterns and practices.',
+ date: '2023-10-17',
+ time: '16:00 - 17:30',
+ category: 'talk',
+ tags: ['cloud', 'development', 'patterns'],
},
{
id: 11,
- title: "Machine Learning for Web Developers",
- speaker: "Jennifer White",
- description: "Introduction to machine learning concepts and how they apply to web development.",
- date: "2023-10-18",
- time: "10:00 - 11:30",
- category: "workshop",
- tags: ["ml", "ai", "web"]
+ title: 'Machine Learning for Web Developers',
+ speaker: 'Jennifer White',
+ description: 'Introduction to machine learning concepts and how they apply to web development.',
+ date: '2023-10-18',
+ time: '10:00 - 11:30',
+ category: 'workshop',
+ tags: ['ml', 'ai', 'web'],
},
{
id: 12,
- title: "DevOps Practices for Modern Teams",
- speaker: "Christopher Lee",
- description: "Effective DevOps practices to improve team productivity and deployment reliability.",
- date: "2023-10-18",
- time: "13:00 - 14:30",
- category: "panel",
- tags: ["devops", "ci/cd", "teams"]
- }
+ title: 'DevOps Practices for Modern Teams',
+ speaker: 'Christopher Lee',
+ description: 'Effective DevOps practices to improve team productivity and deployment reliability.',
+ date: '2023-10-18',
+ time: '13:00 - 14:30',
+ category: 'panel',
+ tags: ['devops', 'ci/cd', 'teams'],
+ },
];
} else {
// Fetch data from the provided endpoint
@@ -223,6 +328,7 @@ export default function decorate(block) {
filteredSessions = [...allSessions];
renderSessions();
} catch (error) {
+ // eslint-disable-next-line no-console
console.error('Error fetching sessions:', error);
// Create a fallback UI with error message
const errorDiv = document.createElement('div');
@@ -233,116 +339,10 @@ export default function decorate(block) {
}
}
- // Function to filter sessions based on search term
- function filterSessions() {
- const searchTerm = searchInput.value.toLowerCase();
- const categoryFilterValue = categoryFilter.value;
-
- filteredSessions = allSessions.filter(session => {
- const matchesSearch =
- (session.title && session.title.toLowerCase().includes(searchTerm)) ||
- (session.speaker && session.speaker.toLowerCase().includes(searchTerm)) ||
- (session.description && session.description.toLowerCase().includes(searchTerm));
-
- const matchesCategory = categoryFilterValue ? session.category === categoryFilterValue : true;
-
- return matchesSearch && matchesCategory;
- });
-
- currentPage = 1;
- renderSessions();
- }
-
- // Function to sort sessions
- function sortSessions() {
- const sortBy = sortBySelect.value;
-
- filteredSessions.sort((a, b) => {
- switch (sortBy) {
- case 'date':
- return new Date(a.date) - new Date(b.date);
- case 'title':
- return (a.title || '').localeCompare(b.title || '');
- case 'speaker':
- return (a.speaker || '').localeCompare(b.speaker || '');
- default:
- return 0;
- }
- });
-
- renderSessions();
- }
-
- // Function to render sessions with pagination
- function renderSessions() {
- // Clear the grid
- sessionGrid.innerHTML = '';
-
- // Calculate pagination
- const totalPages = Math.ceil(filteredSessions.length / itemsPerPage);
- const startIndex = (currentPage - 1) * itemsPerPage;
- const endIndex = startIndex + itemsPerPage;
- const currentSessions = filteredSessions.slice(startIndex, endIndex);
-
- // Update pagination controls
- prevButton.disabled = currentPage === 1;
- nextButton.disabled = currentPage === totalPages || totalPages === 0;
- pageDisplay.textContent = `Page ${currentPage} of ${totalPages || 1}`;
-
- // Render session cards
- currentSessions.forEach(session => {
- const sessionCard = document.createElement('div');
- sessionCard.className = 'session-card';
-
- // Create session card content
- sessionCard.innerHTML = `
-
-
- Speaker: ${session.speaker || 'TBD'}
-
-
- ${session.date || 'Date TBD'}
- ${session.time || 'Time TBD'}
-
-
- ${session.description || 'No description available.'}
-
-
- ${session.tags && session.tags.length > 0
- ? session.tags.map(tag => `${tag}`).join('')
- : ''}
-
-
- `;
-
- sessionGrid.appendChild(sessionCard);
- });
-
- // Add event listeners for pagination
- prevButton.addEventListener('click', () => {
- if (currentPage > 1) {
- currentPage--;
- renderSessions();
- }
- });
-
- nextButton.addEventListener('click', () => {
- if (currentPage < totalPages) {
- currentPage++;
- renderSessions();
- }
- });
-
- // Add event listeners for filters and search
- searchInput.addEventListener('input', filterSessions);
- categoryFilter.addEventListener('change', filterSessions);
- sortBySelect.addEventListener('change', sortSessions);
- }
+ // Add event listeners for filters and search
+ searchInput.addEventListener('input', filterSessions);
+ categoryFilter.addEventListener('change', filterSessions);
+ sortBySelect.addEventListener('change', sortSessions);
// Fetch and display sessions
fetchSessions();
@@ -350,4 +350,4 @@ export default function decorate(block) {
// Replace the block with the session list
block.innerHTML = '';
block.appendChild(sessionListContainer);
-}
\ No newline at end of file
+}