diff --git a/blocks/session-list/session-list.css b/blocks/session-list/session-list.css
index 43911a6..c2fc0fb 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: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);
}
-.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 rgb(0 0 0 / 10%);
+ 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 rgb(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..6135944 100644
--- a/blocks/session-list/session-list.js
+++ b/blocks/session-list/session-list.js
@@ -1,100 +1,351 @@
/*
* 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 || 'https://raw.githubusercontent.com/initialyze/initialyzers-aem-data/refs/heads/main/session-catalog.json';
+
// 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;
+
+ const pageDisplay = document.createElement('span');
+ pageDisplay.className = 'session-page-display';
+ pageDisplay.textContent = 'Page 1 of 1';
+
+ 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 render sessions with pagination
+ function renderSessions() {
+ // Clear the grid
+ sessionGrid.innerHTML = '';
- sessionItem.appendChild(dateTimeDiv);
+ // Calculate pagination
+ const totalPages = Math.ceil(filteredSessions.length / itemsPerPage);
+ const startIndex = (currentPage - 1) * itemsPerPage;
+ const endIndex = startIndex + itemsPerPage;
+ const currentSessions = filteredSessions.slice(startIndex, endIndex);
- // Add speaker
- if (speakerCell.textContent.trim()) {
- const speakerDiv = document.createElement('div');
- speakerDiv.className = 'session-speaker';
- speakerDiv.textContent = speakerCell.textContent.trim();
- sessionItem.appendChild(speakerDiv);
+ // 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();
}
+ });
- // Add description
- if (descriptionCell.textContent.trim()) {
- const descDiv = document.createElement('div');
- descDiv.className = 'session-description';
- descDiv.textContent = descriptionCell.textContent.trim();
- sessionItem.appendChild(descDiv);
+ 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;
- // Add tags
- if (tagsCell && tagsCell.textContent.trim()) {
- const tagsDiv = document.createElement('div');
- tagsDiv.className = 'session-tags';
+ return matchesSearch && matchesCategory;
+ });
- 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);
- });
+ currentPage = 1;
+ renderSessions();
+ }
- sessionItem.appendChild(tagsDiv);
+ // 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 {
+ 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 || []);
}
+
+ // Process the data
+ 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');
+ errorDiv.className = 'session-error';
+ errorDiv.textContent = 'Failed to load sessions. Please try again later.';
+ sessionListContainer.innerHTML = '';
+ sessionListContainer.appendChild(errorDiv);
}
+ }
+
+ // 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 = '';
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/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 5bd434b..9685f8d 100644
--- a/ue/models/_section.json
+++ b/ue/models/_section.json
@@ -55,7 +55,7 @@
"image",
"quote",
"search",
- "speaker",
+ "session-list",
"tabs",
"table",
"text",
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": []
+}
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