diff --git a/.github/workflows/sync-branches.yml b/.github/workflows/sync-branches.yml new file mode 100644 index 00000000..386e1253 --- /dev/null +++ b/.github/workflows/sync-branches.yml @@ -0,0 +1,81 @@ +name: Sync 02 branches + +on: + push: + branches: + - dev + - stage + - main + +jobs: + sync-dev02: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/dev' + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Git + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + + - name: Fetch all branches + run: git fetch --all + + - name: Check out dev02 branch + run: git checkout dev02 + + - name: Merge dev into dev02 with theirs strategy + run: git merge origin/dev --strategy-option theirs --allow-unrelated-histories + + - name: Push changes to dev02 + run: git push origin dev02 + + sync-stage02: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/stage' + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Git + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + + - name: Fetch all branches + run: git fetch --all + + - name: Check out stage02 branch + run: git checkout stage02 + + - name: Merge stage into stage02 with theirs strategy + run: git merge origin/stage --strategy-option theirs --allow-unrelated-histories + + - name: Push changes to stage02 + run: git push origin stage02 + + sync-main02: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Git + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + + - name: Fetch all branches + run: git fetch --all + + - name: Check out main02 branch + run: git checkout main02 + + - name: Merge main into main02 with theirs strategy + run: git merge origin/main --strategy-option theirs --allow-unrelated-histories + + - name: Push changes to main02 + run: git push origin main02 diff --git a/ecc/blocks/attendee-management-table/attendee-management-table.css b/ecc/blocks/attendee-management-table/attendee-management-table.css index e902ce51..a00941fa 100644 --- a/ecc/blocks/attendee-management-table/attendee-management-table.css +++ b/ecc/blocks/attendee-management-table/attendee-management-table.css @@ -1,7 +1,12 @@ .attendee-management-table { font-family: var(--body-font-family); - padding: 40px; + max-width: 1440px; + padding: 0 40px 40px; margin: auto; + + --color-red: #EB1000; + --table-col-width: 120px; + --table-col-padding: 16px; } .attendee-management-table .loading-screen { @@ -22,10 +27,82 @@ font-size: var(--type-body-s-size); } -.attendee-management-table .dashboard-main-container { +.attendee-management-table .dashboard-header-heading { + display: flex; + align-items: center; + gap: 8px; +} + +.attendee-management-table .dashboard-header-heading sp-action-button { + padding: 0; + background: none; + border: none; + cursor: help; +} + +.attendee-management-table .dashboard-main-container .dashboard-body-container { margin-top: 24px; display: flex; - gap: 24px; + gap: 2rem; +} + +.attendee-management-table .dashboard-header-event-info { + display: flex; + align-items: center; + gap: 2rem; +} + +.attendee-management-table .event-info-container { + width: 100%; +} + +.attendee-management-table .dashboard-main-container .event-image { + max-height: 167px; + max-width: 224px; +} + +.attendee-management-table .dashboard-main-container .event-info-row { + display: flex; + justify-content: flex-start; + gap: 25%; + border-bottom: 1px solid var(--color-gray-500); +} + +.attendee-management-table .dashboard-main-container .event-info-label { + font-weight: 700; + margin-right: 1rem; +} + +.attendee-management-table .dashboard-main-container .event-stats-col-wrapper { + display: flex; + align-items: flex-start; +} + +.attendee-management-table .dashboard-main-container .event-stats-row { + height: 64px; + display: flex; + align-items: center; + justify-content: flex-start; + gap: 25%; + margin-top: 1rem; +} + +.attendee-management-table .dashboard-main-container .event-stats-label { + margin: 0; + margin-right: 1rem; + color: var(--color-gray-500) +} + +.attendee-management-table .dashboard-main-container .event-stats-value { + font-size: var(--type-heading-xl-size); + font-weight: 700; + color: var(--color-red); + margin: 0; +} + +.attendee-management-table .dashboard-main-container .event-stats-subtext { + font-size: var(--type-body-xxs-size); + margin: 0; } .attendee-management-table .dashboard-main-container .dashboard-side-panel { @@ -64,15 +141,8 @@ margin: 16px 0; } -.attendee-management-table .dashboard-header { - display: flex; - flex-direction: column; - gap: 16px; - justify-content: space-between; - align-items: flex-start; -} - .attendee-management-table .dashboard-table-container { + width: 100%; max-width: calc(100% - 250px); position: relative; } @@ -159,10 +229,6 @@ gap: 16px; } -.attendee-management-table .dashboard-header-text h1 { - margin: 0; -} - .attendee-management-table .dashboard-header-text p { margin: 6px 0; } @@ -171,6 +237,11 @@ display: flex; align-items: center; gap: 16px; + justify-content: flex-end; +} + +.attendee-management-table.no-results .dashboard-actions-container { + visibility: hidden; } .attendee-management-table .dashboard-actions-container .search-input-wrapper { @@ -205,20 +276,44 @@ } .attendee-management-table table .table-header-row { - height: 40; + height: 56px; border-bottom: 2px solid var(--color-gray-600); } +.attendee-management-table.no-results table .table-header-row { + display: none; +} + .attendee-management-table table .table-header-row th { - padding: 0 16px; + padding: 0 var(--table-col-padding); font-weight: 700; text-align: left; font-size: var(--type-body-xxs-size); color: var(--spectrum-color-gray-500); - width: 100px; + width: var(--table-col-width); white-space: nowrap; } +.attendee-management-table table .table-header-row th.sticky-right-1, +.attendee-management-table table .attendee-row td.sticky-right-1 { + position: sticky; + right: 0; + background-color: var(--color-gray-100); + max-width: var(--table-col-width); + min-width: var(--table-col-width); + z-index: 1; +} + +.attendee-management-table table .table-header-row th.sticky-right-2, +.attendee-management-table table .attendee-row td.sticky-right-2 { + position: sticky; + right: calc(var(--table-col-width) + (var(--table-col-padding) * 2)); + background-color: var(--color-gray-100); + max-width: var(--table-col-width); + min-width: var(--table-col-width); + z-index: 1; +} + .attendee-management-table table .table-header-row th span { white-space: nowrap; width: 60px; @@ -249,6 +344,7 @@ .attendee-management-table table .attendee-row { height: 40px; + border-bottom: 1px solid var(--color-gray-200); } .attendee-management-table table .no-search-results-row td { @@ -256,10 +352,6 @@ text-align: center; } -.attendee-management-table table .attendee-row:nth-of-type(even) { - background-color: var(--color-gray-100); -} - .attendee-management-table table .attendee-row .attendee-title-link { font-weight: 700; text-decoration: none; @@ -351,9 +443,3 @@ gap: 16px; z-index: 9; } - -@media screen and (min-width: 900px) { - .attendee-management-table .dashboard-header { - flex-direction: row; - } -} diff --git a/ecc/blocks/attendee-management-table/attendee-management-table.js b/ecc/blocks/attendee-management-table/attendee-management-table.js index fa4abc1b..106376ee 100644 --- a/ecc/blocks/attendee-management-table/attendee-management-table.js +++ b/ecc/blocks/attendee-management-table/attendee-management-table.js @@ -1,34 +1,96 @@ /* eslint-disable max-len */ import { getAllEventAttendees, getEvents } from '../../scripts/esp-controller.js'; -import { ALLOWED_ACCOUNT_TYPES } from '../../constants/constants.js'; import { LIBS } from '../../scripts/scripts.js'; import { getIcon, buildNoAccessScreen, camelToSentenceCase, readBlockConfig, - getECCEnv, + signIn, + getEventServiceEnv, } from '../../scripts/utils.js'; -import BlockMediator from '../../scripts/deps/block-mediator.min.js'; import { SearchablePicker } from '../../components/searchable-picker/searchable-picker.js'; import { FilterMenu } from '../../components/filter-menu/filter-menu.js'; +import { initProfileLogicTree } from '../../scripts/event-apis.js'; const { createTag } = await import(`${LIBS}/utils/utils.js`); -const ATTENDEE_ATTR_KEYS = [ - 'attendeeId', - 'firstName', - 'lastName', - 'email', - 'companyName', - 'jobTitle', - 'mobilePhone', - 'industry', - 'productsOfInterest', - 'companySize', - 'age', - 'jobLevel', - 'contactMethod', +const ATTENDEE_ATTR_MAP = [ + { + key: 'firstName', + label: 'First name', + fallback: '', + }, + { + key: 'lastName', + label: 'Last name', + fallback: '', + }, + { + key: 'email', + label: 'Email', + fallback: '', + }, + { + key: 'companyName', + label: 'Company', + fallback: '', + }, + { + key: 'jobTitle', + label: 'Job title', + fallback: '', + }, + { + key: 'mobilePhone', + label: 'Mobile phone', + fallback: '', + }, + { + key: 'industry', + label: 'Industry', + fallback: '', + }, + { + key: 'productsOfInterest', + label: 'Products of interest', + fallback: '', + }, + { + key: 'companySize', + label: 'Company size', + fallback: '', + }, + { + key: 'age', + label: 'Age', + fallback: '', + }, + { + key: 'jobLevel', + label: 'Job level', + fallback: '', + }, + { + key: 'contactMethod', + label: 'Contact method', + fallback: '', + }, + { + key: 'registrationDate', + label: 'Registration date', + fallback: '', + }, + { + key: 'type', + label: 'RSVP status', + fallback: 'registered', + }, + { + key: 'checkedIn', + label: 'Checked in', + fallback: '-', + }, ]; const FILTER_MAP = { @@ -42,6 +104,23 @@ const FILTER_MAP = { contactMethod: [], }; +const SPECTRUM_COMPONENTS = [ + 'theme', + 'toast', + 'button', + 'dialog', + 'underlay', + 'progress-circle', + 'textfield', + 'picker', + 'divider', + 'overlay', + 'popover', + 'link', + 'tooltip', + 'action-button', +]; + function buildAllFilterMenues(props) { const sidePanel = props.el.querySelector('.dashboard-side-panel'); @@ -172,8 +251,11 @@ async function populateRow(props, index) { const row = createTag('tr', { class: 'attendee-row', 'data-attendee-id': attendee.attendeeId }, '', { parent: tBody }); - ATTENDEE_ATTR_KEYS.forEach((key) => { - createTag('td', {}, attendee[key] || '', { parent: row }); + ATTENDEE_ATTR_MAP.forEach(({ key, fallback }, i, arr) => { + const td = createTag('td', {}, attendee[key] || fallback, { parent: row }); + if (['type', 'checkedIn'].includes(key)) { + td.classList.add(`sticky-right-${arr.length - i}`, 'actions'); + } }); } @@ -236,20 +318,12 @@ function decoratePagination(props, config) { } function initSorting(props, config) { - const thead = props.el.querySelector('thead'); - const thRow = thead.querySelector('tr'); - - ATTENDEE_ATTR_KEYS.forEach((key) => { - const val = camelToSentenceCase(key).toUpperCase(); - const thText = createTag('span', {}, val); - const th = createTag('th', {}, thText, { parent: thRow }); - - th.append(getIcon('chev-down'), getIcon('chev-up')); - th.classList.add('sortable', key); + const sortables = props.el.querySelectorAll('th.sortable'); + sortables.forEach((th) => { th.addEventListener('click', () => { if (!props.filteredData.length) return; - thead.querySelectorAll('th').forEach((h) => { + sortables.forEach((h) => { if (th !== h) { h.classList.remove('active'); } @@ -257,28 +331,50 @@ function initSorting(props, config) { th.classList.add('active'); props.currentSort = { el: th, - field: key, + field: th.dataset.field, }; sortData(props, config); }); }); } -function buildNoResultsScreen(el, config) { +function buildTableHeaders(props, config) { + const thead = props.el.querySelector('thead'); + const thRow = thead.querySelector('tr'); + + ATTENDEE_ATTR_MAP.forEach(({ key, label }, i, arr) => { + const thText = createTag('span', {}, label.toUpperCase()); + const th = createTag('th', {}, thText, { parent: thRow }); + + th.append(getIcon('chev-down'), getIcon('chev-up')); + + if (['type', 'checkedIn'].includes(key)) th.classList.add('actions', `sticky-right-${arr.length - i}`); + th.classList.add('sortable'); + th.dataset.field = key; + }); + + initSorting(props, config); +} + +function buildNoResultsScreen(props, config) { + const tBody = props.el.querySelector('table.dashboard-table tbody'); + props.el.classList.add('no-results'); + const noSearchResultsRow = createTag('tr', { class: 'no-search-results-row' }); const noSearchResultsCol = createTag('td', { colspan: '100%' }, getIcon('empty-dashboard'), { parent: noSearchResultsRow }); createTag('h2', {}, config['no-attendee-results-heading'], { parent: noSearchResultsCol }); createTag('p', {}, config['no-attendee-results-text'], { parent: noSearchResultsCol }); - el.append(noSearchResultsRow); + tBody.append(noSearchResultsRow); } function populateTable(props, config) { const tBody = props.el.querySelector('table.dashboard-table tbody'); + props.el.classList.remove('no-results'); tBody.innerHTML = ''; if (!props.paginatedData.length) { - buildNoResultsScreen(tBody, config); + buildNoResultsScreen(props, config); } else { const endOfPage = Math.min(+config['page-size'], props.paginatedData.length); @@ -294,7 +390,7 @@ function populateTable(props, config) { function filterData(props, config) { const q = props.currentQuery.toLowerCase(); props.filteredData = props.data.filter((e) => { - const searchMatch = ATTENDEE_ATTR_KEYS.some((key) => e[key]?.toString().toLowerCase().includes(q)); + const searchMatch = ATTENDEE_ATTR_MAP.some(({ key }) => e[key]?.toString().toLowerCase().includes(q)); const appliedFilters = Object.entries(props.currentFilters).filter(([, val]) => val.length); const filterMatch = appliedFilters.every(([key, val]) => val.includes(e[key])); @@ -306,13 +402,84 @@ function filterData(props, config) { sortData(props, config, { resort: true }); } +function calculatePercentage(part, total) { + if (total === 0) { + return '0%'; + } + const percentage = (part / total) * 100; + return `${percentage.toFixed(2)}%`; +} + +function buildEventInfo(props) { + const eventInfoContainer = props.el.querySelector('.dashboard-header-event-info'); + if (!eventInfoContainer) return; + + eventInfoContainer.innerHTML = ''; + const eventInfo = props.events.find((e) => e.eventId === props.currentEventId); + + if (!eventInfo) return; + const heroImgObj = eventInfo.photos?.find((p) => p.imageKind === 'event-hero-image'); + + // build event image + createTag( + 'div', + { class: 'event-image-container' }, + createTag('img', { class: 'event-image', src: heroImgObj ? heroImgObj.sharepointUrl || heroImgObj.imageUrl : '' }), + { parent: eventInfoContainer }, + ); + + const infoContainer = createTag('div', { class: 'event-info-container' }, '', { parent: eventInfoContainer }); + const infoRow = createTag('div', { class: 'event-info-row' }, '', { parent: infoContainer }); + const statsRow = createTag('div', { class: 'event-stats-row' }, '', { parent: infoContainer }); + + [ + { + label: 'EVENT:', + value: eventInfo.title, + }, + { + label: 'WHEN:', + value: new Date(eventInfo.localStartTimeMillis).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }), + }, + { + label: 'TYPE:', + value: eventInfo.eventType, + }, + ].forEach(({ label, value }) => { + const infoColWrapper = createTag('div', { class: 'event-stats-col-wrapper' }, '', { parent: infoRow }); + createTag('span', { class: 'event-info-label' }, label, { parent: infoColWrapper }); + createTag('span', { class: 'event-info-value' }, value, { parent: infoColWrapper }); + }); + + [ + { + label: 'RSVPs', + value: eventInfo.attendeeCount || '0', + subText: calculatePercentage(+eventInfo.attendeeCount, +eventInfo.attendeeLimit), + }, + ].forEach(({ label, value, subText }) => { + const statsColWrapper = createTag('div', { class: 'event-stats-col-wrapper' }, '', { parent: statsRow }); + createTag('h3', { class: 'event-stats-label' }, label, { parent: statsColWrapper }); + const statsValWrapper = createTag('div', { class: 'event-stats-value-wrapper' }, '', { parent: statsColWrapper }); + createTag('p', { class: 'event-stats-value' }, value, { parent: statsValWrapper }); + createTag('p', { class: 'event-stats-subtext' }, subText, { parent: statsValWrapper }); + }); +} + function buildDashboardHeader(props, config) { + const mainContainer = props.el.querySelector('.dashboard-main-container'); const dashboardHeader = createTag('div', { class: 'dashboard-header' }); - const textContainer = createTag('div', { class: 'dashboard-header-text' }); + const headingContainer = createTag('div', { class: 'dashboard-header-text' }); + const eventInfoContainer = createTag('div', { class: 'dashboard-header-event-info' }); const actionsContainer = createTag('div', { class: 'dashboard-actions-container' }); - createTag('h1', { class: 'dashboard-header-heading' }, 'All event attendees', { parent: textContainer }); - createTag('p', { class: 'dashboard-header-attendees-count' }, `(${props.data.length} attendees)`, { parent: textContainer }); + const heading = createTag('h1', { class: 'dashboard-header-heading' }, 'Event report', { parent: headingContainer }); + + if (config.tooltipText) { + const toolTipTrigger = createTag('sp-action-button', { size: 's' }, getIcon('info')); + createTag('sp-tooltip', { 'self-managed': true, variant: 'info' }, config.tooltipText, { parent: toolTipTrigger }); + heading.append(toolTipTrigger); + } const searchInputWrapper = createTag('div', { class: 'search-input-wrapper' }, '', { parent: actionsContainer }); const searchInput = createTag('input', { type: 'text', placeholder: 'Search' }, '', { parent: searchInputWrapper }); @@ -322,28 +489,23 @@ function buildDashboardHeader(props, config) { filterData(props, config); }); - dashboardHeader.append(textContainer, actionsContainer); - props.el.prepend(dashboardHeader); -} - -function updateDashboardHeader(props) { - const attendeesCount = props.el.querySelector('.dashboard-header-attendees-count'); - - if (attendeesCount) attendeesCount.textContent = `(${props.data.length} attendees)`; + dashboardHeader.append(headingContainer, eventInfoContainer, actionsContainer); + mainContainer.prepend(dashboardHeader); + buildEventInfo(props); } function buildDashboardTable(props, config) { - const mainContainer = props.el.querySelector('.dashboard-main-container'); + const dashboardBody = props.el.querySelector('.dashboard-body-container'); - if (!mainContainer) return; + if (!dashboardBody) return; - const tableContainer = createTag('div', { class: 'dashboard-table-container' }, '', { parent: mainContainer }); + const tableContainer = createTag('div', { class: 'dashboard-table-container' }, '', { parent: dashboardBody }); const tableWrapper = createTag('div', { class: 'dashboard-table-wrapper' }, '', { parent: tableContainer }); const table = createTag('table', { class: 'dashboard-table' }, '', { parent: tableWrapper }); const thead = createTag('thead', {}, '', { parent: table }); createTag('tbody', {}, '', { parent: table }); createTag('tr', { class: 'table-header-row' }, '', { parent: thead }); - initSorting(props, config); + buildTableHeaders(props, config); populateTable(props, config); } @@ -376,10 +538,10 @@ function buildEventPicker(props) { const sidePanel = props.el.querySelector('.dashboard-side-panel'); const eventsPickerWrapper = createTag('div', { class: 'events-picker-wrapper' }, '', { parent: sidePanel }); - createTag('sp-field-label', {}, 'Current event', { parent: eventsPickerWrapper }); + createTag('sp-field-label', {}, 'Search other events', { parent: eventsPickerWrapper }); const eventsPicker = createTag('searchable-picker', { class: 'events-picker', - label: 'Choose an event', + label: 'Event name', }, '', { parent: eventsPickerWrapper }); if (props.currentEventId) { @@ -424,11 +586,11 @@ function buildBackToDashboardBtn(props, config) { } function buildDashboardSidePanel(props, config) { - const mainContainer = props.el.querySelector('.dashboard-main-container'); + const dashboardBody = props.el.querySelector('.dashboard-body-container'); - if (!mainContainer) return; + if (!dashboardBody) return; - const sidePanel = createTag('div', { class: 'dashboard-side-panel' }, '', { parent: mainContainer }); + const sidePanel = createTag('div', { class: 'dashboard-side-panel' }, '', { parent: dashboardBody }); buildBackToDashboardBtn(props, config); buildEventPicker(props); createTag('sp-divider', {}, '', { parent: sidePanel }); @@ -459,7 +621,8 @@ async function buildDashboard(el, config) { const spTheme = createTag('sp-theme', { color: 'light', scale: 'medium', class: 'toast-area' }, '', { parent: el }); createTag('sp-underlay', {}, '', { parent: spTheme }); createTag('sp-dialog', { size: 's' }, '', { parent: spTheme }); - createTag('sp-theme', { color: 'light', scale: 'medium', class: 'dashboard-main-container' }, '', { parent: el }); + const mainContainer = createTag('sp-theme', { color: 'light', scale: 'medium', class: 'dashboard-main-container' }, '', { parent: el }); + createTag('div', { class: 'dashboard-body-container' }, '', { parent: mainContainer }); const uspEventId = new URLSearchParams(window.location.search).get('eventId'); const events = await getEventsArray(); @@ -497,6 +660,7 @@ async function buildDashboard(el, config) { target.currentFilters = {}; updateFilterMap(receiver); buildFilters(receiver); + buildEventInfo(target); } if (prop === 'currentEventId') { @@ -508,7 +672,6 @@ async function buildDashboard(el, config) { filterData(target, config); } - updateDashboardHeader(target); populateTable(receiver, config); updateResetFilterBtnState(target); @@ -538,20 +701,12 @@ function buildLoadingScreen(el) { export default async function init(el) { const miloLibs = LIBS; + const promises = Array.from(SPECTRUM_COMPONENTS).map(async (component) => { + await import(`${miloLibs}/features/spectrum-web-components/dist/${component}.js`); + }); await Promise.all([ import(`${miloLibs}/deps/lit-all.min.js`), - import(`${miloLibs}/features/spectrum-web-components/dist/theme.js`), - import(`${miloLibs}/features/spectrum-web-components/dist/toast.js`), - import(`${miloLibs}/features/spectrum-web-components/dist/button.js`), - import(`${miloLibs}/features/spectrum-web-components/dist/dialog.js`), - import(`${miloLibs}/features/spectrum-web-components/dist/underlay.js`), - import(`${miloLibs}/features/spectrum-web-components/dist/progress-circle.js`), - import(`${miloLibs}/features/spectrum-web-components/dist/textfield.js`), - import(`${miloLibs}/features/spectrum-web-components/dist/picker.js`), - import(`${miloLibs}/features/spectrum-web-components/dist/divider.js`), - import(`${miloLibs}/features/spectrum-web-components/dist/overlay.js`), - import(`${miloLibs}/features/spectrum-web-components/dist/popover.js`), - import(`${miloLibs}/features/spectrum-web-components/dist/link.js`), + ...promises, ]); const config = readBlockConfig(el); @@ -560,32 +715,20 @@ export default async function init(el) { const sp = new URLSearchParams(window.location.search); const devToken = sp.get('devToken'); - if (devToken && getECCEnv() === 'dev') { + if (devToken && getEventServiceEnv() === 'dev') { buildDashboard(el, config); return; } - const profile = BlockMediator.get('imsProfile'); - - if (profile) { - if (profile.noProfile || !ALLOWED_ACCOUNT_TYPES.includes(profile.account_type)) { + initProfileLogicTree({ + noProfile: () => { + signIn(); + }, + noAccessProfile: () => { buildNoAccessScreen(el); - } else { + }, + validProfile: () => { buildDashboard(el, config); - } - - return; - } - - if (!profile) { - const unsubscribe = BlockMediator.subscribe('imsProfile', ({ newValue }) => { - if (newValue?.noProfile || !ALLOWED_ACCOUNT_TYPES.includes(newValue.account_type)) { - buildNoAccessScreen(el); - } else { - buildDashboard(el, config); - } - - unsubscribe(); - }); - } + }, + }); } diff --git a/ecc/blocks/ecc-dashboard/ecc-dashboard.js b/ecc/blocks/ecc-dashboard/ecc-dashboard.js index 101868c0..fad59d6a 100644 --- a/ecc/blocks/ecc-dashboard/ecc-dashboard.js +++ b/ecc/blocks/ecc-dashboard/ecc-dashboard.js @@ -6,11 +6,17 @@ import { publishEvent, unpublishEvent, } from '../../scripts/esp-controller.js'; -import { ALLOWED_ACCOUNT_TYPES } from '../../constants/constants.js'; import { LIBS } from '../../scripts/scripts.js'; -import { getIcon, buildNoAccessScreen, getEventPageHost, readBlockConfig, getECCEnv } from '../../scripts/utils.js'; +import { + getIcon, + buildNoAccessScreen, + getEventPageHost, + readBlockConfig, + signIn, + getEventServiceEnv, +} from '../../scripts/utils.js'; import { quickFilter } from '../form-handler/data-handler.js'; -import BlockMediator from '../../scripts/deps/block-mediator.min.js'; +import { initProfileLogicTree } from '../../scripts/event-apis.js'; const { createTag } = await import(`${LIBS}/utils/utils.js`); @@ -602,6 +608,11 @@ function buildDashboardHeader(props, config) { props.el.prepend(dashboardHeader); } +function updateEventsCount(props) { + const eventsCount = props.el.querySelector('.dashboard-header-events-count'); + eventsCount.textContent = `(${props.data.length} events)`; +} + function buildDashboardTable(props, config) { const tableContainer = createTag('div', { class: 'dashboard-table-container' }, '', { parent: props.el }); const table = createTag('table', { class: 'dashboard-table' }, '', { parent: tableContainer }); @@ -667,7 +678,7 @@ async function buildDashboard(el, config) { set(target, prop, value, receiver) { target[prop] = value; populateTable(receiver, config); - + updateEventsCount(receiver); return true; }, }; @@ -708,32 +719,20 @@ export default async function init(el) { const sp = new URLSearchParams(window.location.search); const devToken = sp.get('devToken'); - if (devToken && getECCEnv() === 'dev') { + if (devToken && getEventServiceEnv() === 'dev') { buildDashboard(el, config); return; } - const profile = BlockMediator.get('imsProfile'); - - if (profile) { - if (profile.noProfile || !ALLOWED_ACCOUNT_TYPES.includes(profile.account_type)) { + initProfileLogicTree({ + noProfile: () => { + signIn(); + }, + noAccessProfile: () => { buildNoAccessScreen(el); - } else { + }, + validProfile: () => { buildDashboard(el, config); - } - - return; - } - - if (!profile) { - const unsubscribe = BlockMediator.subscribe('imsProfile', ({ newValue }) => { - if (newValue?.noProfile || !ALLOWED_ACCOUNT_TYPES.includes(newValue.account_type)) { - buildNoAccessScreen(el); - } else { - buildDashboard(el, config); - } - - unsubscribe(); - }); - } + }, + }); } diff --git a/ecc/blocks/form-handler/controllers/event-agenda-component-controller.js b/ecc/blocks/form-handler/controllers/event-agenda-component-controller.js index 59759f2d..097c77d2 100644 --- a/ecc/blocks/form-handler/controllers/event-agenda-component-controller.js +++ b/ecc/blocks/form-handler/controllers/event-agenda-component-controller.js @@ -7,7 +7,7 @@ export function onSubmit(component, props) { let agenda = []; - if (agendaGroup) agenda = agendaGroup.getAgendas(); + if (agendaGroup) agenda = agendaGroup.getCompleteAgenda(); const agendaInfo = { showAgendaPostEvent, diff --git a/ecc/blocks/form-handler/controllers/event-community-link-component-controller.js b/ecc/blocks/form-handler/controllers/event-community-link-component-controller.js index ac54ce5b..2156bc87 100644 --- a/ecc/blocks/form-handler/controllers/event-community-link-component-controller.js +++ b/ecc/blocks/form-handler/controllers/event-community-link-component-controller.js @@ -7,7 +7,7 @@ export function onSubmit(component, props) { const checkbox = component.querySelector('#checkbox-community'); if (checkbox.checked) { - const communityTopicUrl = component.querySelector('#community-url-details').value; + const communityTopicUrl = component.querySelector('#community-url-details')?.value?.trim(); props.payload = { ...props.payload, communityTopicUrl }; } else { const tempPayload = { ...props.payload }; diff --git a/ecc/blocks/form-handler/controllers/registration-details-component-controller.js b/ecc/blocks/form-handler/controllers/registration-details-component-controller.js index cde7c625..97f52334 100644 --- a/ecc/blocks/form-handler/controllers/registration-details-component-controller.js +++ b/ecc/blocks/form-handler/controllers/registration-details-component-controller.js @@ -41,20 +41,20 @@ function prefillFields(component, props) { export function onSubmit(component, props) { if (component.closest('.fragment')?.classList.contains('hidden')) return; - const attendeeLimitVal = component.querySelector('#attendee-count-input')?.value; + const attendeeLimitVal = component.querySelector('#attendee-count-input')?.value?.trim(); const allowWaitlisting = component.querySelector('#registration-allow-waitlist')?.checked; const contactHost = component.querySelector('#registration-contact-host')?.checked; - const hostEmail = component.querySelector('#event-host-email-input')?.value; + const hostEmail = component.querySelector('#event-host-email-input')?.value?.trim(); const rsvpDescription = component.querySelector('#rsvp-form-detail-description')?.value; const attendeeLimit = Number.isNaN(+attendeeLimitVal) ? null : +attendeeLimitVal; const rsvpData = {}; - if (rsvpDescription) rsvpData.rsvpDescription = rsvpDescription; + rsvpData.rsvpDescription = rsvpDescription || ''; + rsvpData.allowWaitlisting = !!allowWaitlisting; if (contactHost && hostEmail) rsvpData.hostEmail = hostEmail; if (attendeeLimit) rsvpData.attendeeLimit = attendeeLimit; - if (allowWaitlisting) rsvpData.allowWaitlisting = allowWaitlisting; props.payload = { ...props.payload, ...rsvpData }; } diff --git a/ecc/blocks/form-handler/controllers/terms-conditions-component-controller.js b/ecc/blocks/form-handler/controllers/terms-conditions-component-controller.js index 9c9f7303..0d36d1f6 100644 --- a/ecc/blocks/form-handler/controllers/terms-conditions-component-controller.js +++ b/ecc/blocks/form-handler/controllers/terms-conditions-component-controller.js @@ -1,7 +1,7 @@ /* eslint-disable no-unused-vars */ import { LIBS } from '../../../scripts/scripts.js'; import HtmlSanitizer from '../../../scripts/deps/html-sanitizer.js'; -import { fetchThrottledMemoizedText, getECCEnv } from '../../../scripts/utils.js'; +import { fetchThrottledMemoizedText, getEventServiceEnv } from '../../../scripts/utils.js'; const { createTag } = await import(`${LIBS}/utils/utils.js`); @@ -35,7 +35,7 @@ async function loadPreview(component, templateId) { let host; if (window.location.href.includes('.hlx.')) { - host = window.location.origin.replace(window.location.hostname, `${getECCEnv()}--events-milo--adobecom.hlx.page`); + host = window.location.origin.replace(window.location.hostname, `${getEventServiceEnv()}--events-milo--adobecom.hlx.page`); } else { host = window.location.origin; } diff --git a/ecc/blocks/form-handler/controllers/venue-info-component-controller.js b/ecc/blocks/form-handler/controllers/venue-info-component-controller.js index 4b3a894b..02c96ee0 100644 --- a/ecc/blocks/form-handler/controllers/venue-info-component-controller.js +++ b/ecc/blocks/form-handler/controllers/venue-info-component-controller.js @@ -1,24 +1,26 @@ /* eslint-disable no-unused-vars */ import { createVenue, replaceVenue } from '../../../scripts/esp-controller.js'; import BlockMediator from '../../../scripts/deps/block-mediator.min.js'; -import { changeInputValue, getECCEnv, getSecret } from '../../../scripts/utils.js'; +import { changeInputValue, getEventServiceEnv, getSecret } from '../../../scripts/utils.js'; import { buildErrorMessage } from '../form-handler.js'; -function togglePrefillableFieldsHiddenState(component, showPrefilledFields) { - const addressInput = component.querySelector('#venue-info-venue-address'); - const cityInput = component.querySelector('#location-city'); - const stateInput = component.querySelector('#location-state'); - const postalCodeInput = component.querySelector('#location-zip-code'); - const countryInput = component.querySelector('#location-country'); +function togglePrefillableFieldsHiddenState(component) { + const address = component.querySelector('#venue-info-venue-address'); + const city = component.querySelector('#location-city'); + const state = component.querySelector('#location-state'); + const postal = component.querySelector('#location-zip-code'); + const county = component.querySelector('#location-country'); - [addressInput, cityInput, stateInput, postalCodeInput, countryInput].forEach((input) => { - input.classList.toggle('hidden', showPrefilledFields); + const hasUnfilledFields = [address, city, state, postal, county].some((input) => !input.value); + + [address, city, state, postal, county].forEach((input) => { + input.classList.toggle('hidden', hasUnfilledFields); }); } async function loadGoogleMapsAPI(callback) { const script = document.createElement('script'); - const apiKey = await getSecret(`${getECCEnv()}-google-places-api`); + const apiKey = await getSecret(`${getEventServiceEnv()}-google-places-api`); script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places&callback=onGoogleMapsApiLoaded`; script.async = true; script.defer = true; @@ -29,6 +31,109 @@ async function loadGoogleMapsAPI(callback) { document.head.appendChild(script); } +function resetAllFields(component) { + const venueNameInput = component.querySelector('#venue-info-venue-name'); + const addressInput = component.querySelector('#venue-info-venue-address'); + const cityInput = component.querySelector('#location-city'); + const stateInput = component.querySelector('#location-state'); + const stateCodeInput = component.querySelector('#location-state-code'); + const postalCodeInput = component.querySelector('#location-zip-code'); + const countryInput = component.querySelector('#location-country'); + const placeLatInput = component.querySelector('#google-place-lat'); + const placeLngInput = component.querySelector('#google-place-lng'); + const placeIdInput = component.querySelector('#google-place-id'); + const mapUrlInput = component.querySelector('#google-map-url'); + const gmtoffsetInput = component.querySelector('#google-place-gmt-offset'); + + venueNameInput.value = ''; + changeInputValue(addressInput, 'value', ''); + changeInputValue(cityInput, 'value', ''); + changeInputValue(stateInput, 'value', ''); + changeInputValue(stateCodeInput, 'value', ''); + changeInputValue(postalCodeInput, 'value', ''); + changeInputValue(countryInput, 'value', ''); + changeInputValue(placeLatInput, 'value', ''); + changeInputValue(placeLngInput, 'value', ''); + changeInputValue(placeIdInput, 'value', ''); + changeInputValue(mapUrlInput, 'value', ''); + changeInputValue(gmtoffsetInput, 'value', ''); +} + +function updateAllFields(venueData, component) { + const venueNameInput = component.querySelector('#venue-info-venue-name'); + const addressInput = component.querySelector('#venue-info-venue-address'); + const cityInput = component.querySelector('#location-city'); + const stateInput = component.querySelector('#location-state'); + const stateCodeInput = component.querySelector('#location-state-code'); + const postalCodeInput = component.querySelector('#location-zip-code'); + const countryInput = component.querySelector('#location-country'); + const placeLatInput = component.querySelector('#google-place-lat'); + const placeLngInput = component.querySelector('#google-place-lng'); + const placeIdInput = component.querySelector('#google-place-id'); + const mapUrlInput = component.querySelector('#google-map-url'); + const gmtoffsetInput = component.querySelector('#google-place-gmt-offset'); + + changeInputValue(venueNameInput, 'value', venueData.venueName); + changeInputValue(addressInput, 'value', venueData.address); + changeInputValue(cityInput, 'value', venueData.city); + changeInputValue(stateInput, 'value', venueData.state); + changeInputValue(stateCodeInput, 'value', venueData.statecode); + changeInputValue(postalCodeInput, 'value', venueData.postalCode); + changeInputValue(countryInput, 'value', venueData.country); + changeInputValue(placeLatInput, 'value', venueData.coordinates?.lat); + changeInputValue(placeLngInput, 'value', venueData.coordinates?.lon); + changeInputValue(placeIdInput, 'value', venueData.placeId); + changeInputValue(mapUrlInput, 'value', venueData.mapUrl); + changeInputValue(gmtoffsetInput, 'value', venueData.gmtOffset); +} + +function getVenueDataInForm(component) { + const venueNameInput = component.querySelector('#venue-info-venue-name'); + const addressInput = component.querySelector('#venue-info-venue-address'); + const cityInput = component.querySelector('#location-city'); + const stateInput = component.querySelector('#location-state'); + const stateCodeInput = component.querySelector('#location-state-code'); + const postalCodeInput = component.querySelector('#location-zip-code'); + const countryInput = component.querySelector('#location-country'); + const placeLatInput = component.querySelector('#google-place-lat'); + const placeLngInput = component.querySelector('#google-place-lng'); + const placeIdInput = component.querySelector('#google-place-id'); + const mapUrlInput = component.querySelector('#google-map-url'); + const gmtoffsetInput = component.querySelector('#google-place-gmt-offset'); + + const venueName = venueNameInput.value; + const address = addressInput.value; + const city = cityInput.value; + const state = stateInput.value; + const stateCode = stateCodeInput.value; + const postalCode = postalCodeInput.value; + const country = countryInput.value; + const placeId = placeIdInput.value; + const mapUrl = mapUrlInput.value; + const lat = +placeLatInput.value; + const lon = +placeLngInput.value; + const gmtOffset = +gmtoffsetInput.value; + + const venueData = { + venueName, + address, + city, + state, + stateCode, + postalCode, + country, + placeId, + mapUrl, + coordinates: { + lat, + lon, + }, + gmtOffset, + }; + + return venueData; +} + function initAutocomplete(el, props) { const venueName = el.querySelector('#venue-info-venue-name'); // eslint-disable-next-line no-undef @@ -86,6 +191,13 @@ function initAutocomplete(el, props) { } }); + if (Object.values(addressInfo).some((v) => !v)) { + el.dispatchEvent(new CustomEvent('show-error-toast', { detail: { error: { message: 'The selection is not a valid venue.' } }, bubbles: true, composed: true })); + resetAllFields(el); + togglePrefillableFieldsHiddenState(el); + return; + } + if (place.name) changeInputValue(venueName, 'value', place.name); changeInputValue(address, 'value', addressInfo.address); changeInputValue(city, 'value', addressInfo.city); @@ -96,7 +208,7 @@ function initAutocomplete(el, props) { changeInputValue(placeId, 'value', place.place_id); changeInputValue(mapUrl, 'value', place.url); - togglePrefillableFieldsHiddenState(el, false); + togglePrefillableFieldsHiddenState(el); BlockMediator.set('eventDupMetrics', { ...BlockMediator.get('eventDupMetrics'), city: addressInfo.city }); } @@ -128,66 +240,23 @@ export default async function init(component, props) { const { venue, showVenuePostEvent } = eventData; const venueNameInput = component.querySelector('#venue-info-venue-name'); - const addressInput = component.querySelector('#venue-info-venue-address'); - const cityInput = component.querySelector('#location-city'); - const stateInput = component.querySelector('#location-state'); - const stateCodeInput = component.querySelector('#location-state-code'); - const postalCodeInput = component.querySelector('#location-zip-code'); - const countryInput = component.querySelector('#location-country'); - const placeLatInput = component.querySelector('#google-place-lat'); - const placeLngInput = component.querySelector('#google-place-lng'); - const placeIdInput = component.querySelector('#google-place-id'); - const mapUrlInput = component.querySelector('#google-map-url'); - const gmtoffsetInput = component.querySelector('#google-place-gmt-offset'); - togglePrefillableFieldsHiddenState(component, true); + togglePrefillableFieldsHiddenState(component); venueNameInput.addEventListener('change', () => { if (!venueNameInput.value) { - changeInputValue(addressInput, 'value', ''); - changeInputValue(cityInput, 'value', ''); - changeInputValue(stateInput, 'value', ''); - changeInputValue(stateCodeInput, 'value', ''); - changeInputValue(postalCodeInput, 'value', ''); - changeInputValue(countryInput, 'value', ''); - changeInputValue(placeLatInput, 'value', ''); - changeInputValue(placeLngInput, 'value', ''); - changeInputValue(placeIdInput, 'value', ''); - changeInputValue(mapUrlInput, 'value', ''); - changeInputValue(gmtoffsetInput, 'value', ''); + resetAllFields(component); + togglePrefillableFieldsHiddenState(component, true); } }); if (venue) { - const { - venueName, - address, - city, - state, - statecode, - postalCode, - country, - placeId, - mapUrl, - } = venue; - - changeInputValue(venueNameInput, 'value', venueName); - changeInputValue(addressInput, 'value', address); - changeInputValue(cityInput, 'value', city); - changeInputValue(stateInput, 'value', state); - changeInputValue(stateCodeInput, 'value', statecode); - changeInputValue(postalCodeInput, 'value', postalCode); - changeInputValue(countryInput, 'value', country); - changeInputValue(placeLatInput, 'value', venue.coordinates?.lat); - changeInputValue(placeLngInput, 'value', venue.coordinates?.lon); - changeInputValue(placeIdInput, 'value', placeId); - changeInputValue(mapUrlInput, 'value', mapUrl); - changeInputValue(gmtoffsetInput, 'value', venue.gmtOffset); - BlockMediator.set('eventDupMetrics', { ...BlockMediator.get('eventDupMetrics'), city }); - - if (venueName) { + updateAllFields(venue, component); + BlockMediator.set('eventDupMetrics', { ...BlockMediator.get('eventDupMetrics'), city: venue.city }); + + if (venue.venueName) { component.classList.add('prefilled'); - togglePrefillableFieldsHiddenState(component, false); + togglePrefillableFieldsHiddenState(component); } } @@ -196,53 +265,6 @@ export default async function init(component, props) { } } -const getVenueDataInForm = (component) => { - const venueNameInput = component.querySelector('#venue-info-venue-name'); - const addressInput = component.querySelector('#venue-info-venue-address'); - const cityInput = component.querySelector('#location-city'); - const stateInput = component.querySelector('#location-state'); - const stateCodeInput = component.querySelector('#location-state-code'); - const postalCodeInput = component.querySelector('#location-zip-code'); - const countryInput = component.querySelector('#location-country'); - const placeLatInput = component.querySelector('#google-place-lat'); - const placeLngInput = component.querySelector('#google-place-lng'); - const placeIdInput = component.querySelector('#google-place-id'); - const mapUrlInput = component.querySelector('#google-map-url'); - const gmtoffsetInput = component.querySelector('#google-place-gmt-offset'); - - const venueName = venueNameInput.value; - const address = addressInput.value; - const city = cityInput.value; - const state = stateInput.value; - const stateCode = stateCodeInput.value; - const postalCode = postalCodeInput.value; - const country = countryInput.value; - const placeId = placeIdInput.value; - const mapUrl = mapUrlInput.value; - const lat = +placeLatInput.value; - const lon = +placeLngInput.value; - const gmtOffset = +gmtoffsetInput.value; - - const venueData = { - venueName, - address, - city, - state, - stateCode, - postalCode, - country, - placeId, - mapUrl, - coordinates: { - lat, - lon, - }, - gmtOffset, - }; - - return venueData; -}; - export async function onEventUpdate(component, props) { if (component.closest('.fragment')?.classList.contains('hidden')) return; diff --git a/ecc/blocks/form-handler/form-handler.js b/ecc/blocks/form-handler/form-handler.js index ee73e6bd..67a5b0b3 100644 --- a/ecc/blocks/form-handler/form-handler.js +++ b/ecc/blocks/form-handler/form-handler.js @@ -1,4 +1,3 @@ -import { ALLOWED_ACCOUNT_TYPES } from '../../constants/constants.js'; import { LIBS } from '../../scripts/scripts.js'; import { getIcon, @@ -6,7 +5,8 @@ import { generateToolTip, camelToSentenceCase, getEventPageHost, - getECCEnv, + signIn, + getEventServiceEnv, } from '../../scripts/utils.js'; import { createEvent, @@ -26,8 +26,8 @@ import ProductSelectorGroup from '../../components/product-selector-group/produc import PartnerSelector from '../../components/partner-selector/partner-selector.js'; import PartnerSelectorGroup from '../../components/partner-selector-group/partner-selector-group.js'; import getJoinedData, { getFilteredCachedResponse, quickFilter, setPayloadCache, setResponseCache } from './data-handler.js'; -import BlockMediator from '../../scripts/deps/block-mediator.min.js'; import { CustomSearch } from '../../components/custom-search/custom-search.js'; +import { initProfileLogicTree } from '../../scripts/event-apis.js'; const { createTag } = await import(`${LIBS}/utils/utils.js`); const { decorateButtons } = await import(`${LIBS}/utils/decorate.js`); @@ -104,8 +104,8 @@ export function buildErrorMessage(props, resp) { }); }); } else if (errorMessage) { - if (resp.status === 409) { - const toast = createTag('sp-toast', { open: true, variant: 'negative' }, errorMessage, { parent: toastArea }); + if (resp.status === 409 || resp.error.message === 'Request to ESP failed: {"message":"Event update invalid, event has been modified since last fetch"}') { + const toast = createTag('sp-toast', { open: true, variant: 'negative' }, 'The event has been updated by a different session since your last save.', { parent: toastArea }); const url = new URL(window.location.href); url.searchParams.set('eventId', getFilteredCachedResponse().eventId); @@ -113,7 +113,7 @@ export function buildErrorMessage(props, resp) { slot: 'action', variant: 'overBackground', href: `${url.toString()}`, - }, 'See the latest version.', { parent: toast }); + }, 'See the latest version', { parent: toast }); toast.addEventListener('close', () => { toast.remove(); @@ -860,40 +860,25 @@ export default async function init(el) { const sp = new URLSearchParams(window.location.search); const devToken = sp.get('devToken'); - if (devToken && getECCEnv() === 'dev') { + if (devToken && getEventServiceEnv() === 'dev') { buildECCForm(el).then(() => { el.classList.remove('loading'); }); return; } - const profile = BlockMediator.get('imsProfile'); - - if (profile) { - if (profile.noProfile || !ALLOWED_ACCOUNT_TYPES.includes(profile.account_type)) { + initProfileLogicTree({ + noProfile: () => { + signIn(); + }, + noAccessProfile: () => { buildNoAccessScreen(el); el.classList.remove('loading'); - } else { + }, + validProfile: () => { buildECCForm(el).then(() => { el.classList.remove('loading'); }); - } - - return; - } - - if (!profile) { - const unsubscribe = BlockMediator.subscribe('imsProfile', ({ newValue }) => { - if (newValue?.noProfile || !ALLOWED_ACCOUNT_TYPES.includes(newValue.account_type)) { - buildNoAccessScreen(el); - el.classList.remove('loading'); - unsubscribe(); - } else { - buildECCForm(el).then(() => { - el.classList.remove('loading'); - unsubscribe(); - }); - } - }); - } + }, + }); } diff --git a/ecc/blocks/registration-details-component/registration-details-component.js b/ecc/blocks/registration-details-component/registration-details-component.js index f5e6c550..41135343 100644 --- a/ecc/blocks/registration-details-component/registration-details-component.js +++ b/ecc/blocks/registration-details-component/registration-details-component.js @@ -65,6 +65,7 @@ function decorateAllCheckboxes(el) { id: 'event-host-email-input', class: 'text-input', placeholder: inputText, + type: 'email', size: 's', }); diff --git a/ecc/components/agenda-fieldset-group/agenda-fieldset-group.js b/ecc/components/agenda-fieldset-group/agenda-fieldset-group.js index 598074e2..5ded6fe3 100644 --- a/ecc/components/agenda-fieldset-group/agenda-fieldset-group.js +++ b/ecc/components/agenda-fieldset-group/agenda-fieldset-group.js @@ -40,8 +40,8 @@ export default class AgendaFieldsetGroup extends LitElement { this.agendaItems = this.agendaItems.map((agenda, i) => (i === index ? updatedAgenda : agenda)); } - getAgendas() { - return this.agendaItems.filter((o) => !(Object.keys(o).length === 0 && o.constructor === Object)); + getCompleteAgenda() { + return this.agendaItems.filter((o) => (o.startTime && o.description)); } hasOnlyEmptyAgendaLeft() { diff --git a/ecc/components/image-dropzone/image-dropzone.js b/ecc/components/image-dropzone/image-dropzone.js index 197bc8ce..42a571f1 100644 --- a/ecc/components/image-dropzone/image-dropzone.js +++ b/ecc/components/image-dropzone/image-dropzone.js @@ -1,5 +1,6 @@ /* eslint-disable import/prefer-default-export */ /* eslint-disable class-methods-use-this */ +import { isImageTypeValid, isImageSizeValid } from '../../scripts/image-validator.js'; import { LIBS } from '../../scripts/scripts.js'; import { style } from './image-dropzone.css.js'; @@ -22,16 +23,21 @@ export class ImageDropzone extends LitElement { this.handleDelete = this.handleDelete || null; } - setFile(files) { + async setFile(files) { const [file] = files; - if (file.size > 26214400) { + + if (!isImageSizeValid(file, 26214400)) { this.dispatchEvent(new CustomEvent('show-error-toast', { detail: { error: { message: 'File size should be less than 25MB' } }, bubbles: true, composed: true })); return; } - if (file.type.startsWith('image/')) { + + const isValid = await isImageTypeValid(file); + if (isValid) { this.file = file; this.file.url = URL.createObjectURL(file); this.requestUpdate(); + } else { + this.dispatchEvent(new CustomEvent('show-error-toast', { detail: { error: { message: 'Invalid file type. The image file should be in one of the following format: .jpeg, .jpg, .png, .svg' } }, bubbles: true, composed: true })); } } @@ -39,22 +45,22 @@ export class ImageDropzone extends LitElement { return this.file; } - handleImageDrop(e) { + async handleImageDrop(e) { e.preventDefault(); e.stopPropagation(); const { files } = e.dataTransfer; if (files.length > 0) { - this.setFile(files); + await this.setFile(files); this.handleImage(); } } - onImageChange(e) { + async onImageChange(e) { const { files } = e.currentTarget; if (files.length > 0) { - this.setFile(files); + await this.setFile(files); this.handleImage(); } diff --git a/ecc/components/partner-selector/partner-selector.js b/ecc/components/partner-selector/partner-selector.js index fe1227bf..d0829f6a 100644 --- a/ecc/components/partner-selector/partner-selector.js +++ b/ecc/components/partner-selector/partner-selector.js @@ -149,13 +149,13 @@ export default class PartnerSelector extends LitElement {