From 6d1a20013061ba2956c74ced989f2ef9577c1029 Mon Sep 17 00:00:00 2001 From: Qiyun Dai Date: Mon, 19 Aug 2024 12:35:23 -0500 Subject: [PATCH 1/7] status tag added (#181) --- ecc/blocks/form-handler/form-handler.css | 17 ++++++++++++++ ecc/blocks/form-handler/form-handler.js | 29 ++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/ecc/blocks/form-handler/form-handler.css b/ecc/blocks/form-handler/form-handler.css index 431f42cd..4fd12f21 100644 --- a/ecc/blocks/form-handler/form-handler.css +++ b/ecc/blocks/form-handler/form-handler.css @@ -289,11 +289,24 @@ color: var(--color-red); } +.form-handler .main-frame .section:first-of-type .step-heading-wrapper { + display: flex; + align-items: center; + gap: 16px; +} + +.form-handler .main-frame .section:first-of-type .step-heading-wrapper .event-status-tag { + padding: 0 8px; + background-color: var(--color-white); + border-radius: 4px; +} + .form-handler .main-frame .section:not(:first-of-type) { padding: 24px 56px; border-radius: 10px; margin: 24px; box-shadow: 0 3px 6px 0 rgb(0 0 0 / 16%); + background-color: var(--color-white); } .form-handler .fragment.hidden { @@ -368,6 +381,10 @@ width: 20px; } +.form-handler .main-frame .section:first-of-type .step-heading-wrapper .event-status-tag .icon { + margin-right: 4px; +} + .form-handler .form-handler-ctas-panel .form-handler-forward-wrapper > div:first-of-type { padding-right: 64px; border-right: 1px solid var(--color-black); diff --git a/ecc/blocks/form-handler/form-handler.js b/ecc/blocks/form-handler/form-handler.js index 74b0d56b..e329482e 100644 --- a/ecc/blocks/form-handler/form-handler.js +++ b/ecc/blocks/form-handler/form-handler.js @@ -584,12 +584,15 @@ function initFormCtas(props) { }); backBtn.addEventListener('click', async () => { + toggleBtnsSubmittingState(true); const resp = await saveEvent(props); if (resp?.error) { buildErrorMessage(props, resp); } else { props.currentStep -= 1; } + + toggleBtnsSubmittingState(false); }); } @@ -666,6 +669,29 @@ function initDeepLink(props) { } } +function updateStatusTag(props) { + const { eventDataResp } = props; + + if (eventDataResp?.published === undefined) return; + + const currentFragment = getCurrentFragment(props); + + const headingSection = currentFragment.querySelector(':scope > .section:first-child'); + + const eixstingStatusTag = headingSection.querySelector('.event-status-tag'); + if (eixstingStatusTag) eixstingStatusTag.remove(); + + const heading = headingSection.querySelector('h2', 'h3', 'h3', 'h4'); + const headingWrapper = createTag('div', { class: 'step-heading-wrapper' }); + const dot = eventDataResp.published ? getIcon('dot-purple') : getIcon('dot-green'); + const text = eventDataResp.published ? 'Published' : 'Draft'; + const statusTag = createTag('span', { class: 'event-status-tag' }); + + statusTag.append(dot, text); + heading.parentElement?.replaceChild(headingWrapper, heading); + headingWrapper.append(heading, statusTag); +} + async function buildECCForm(el) { const props = { el, @@ -691,6 +717,7 @@ async function buildECCForm(el) { renderFormNavigation(target, oldValue, value); updateSideNav(target); initRequiredFieldsValidation(target); + updateStatusTag(target); break; } @@ -746,6 +773,8 @@ async function buildECCForm(el) { updateRequiredFields(proxyProps); enableSideNavForEditFlow(proxyProps); initDeepLink(proxyProps); + updateStatusTag(proxyProps); + el.addEventListener('show-error-toast', (e) => { e.stopPropagation(); e.preventDefault(); From 5be18c1e24f227a0877a9c01a6d387fd036d169e Mon Sep 17 00:00:00 2001 From: Qiyun Dai Date: Mon, 19 Aug 2024 16:39:57 -0500 Subject: [PATCH 2/7] [MWPW-156944] Metadata split (#182) --- .../terms-conditions-component-controller.js | 25 +++++++++++-------- ecc/scripts/esp-controller.js | 4 +-- 2 files changed, 17 insertions(+), 12 deletions(-) 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 7d1d1782..c0dcd5e8 100644 --- a/ecc/blocks/form-handler/controllers/terms-conditions-component-controller.js +++ b/ecc/blocks/form-handler/controllers/terms-conditions-component-controller.js @@ -31,6 +31,10 @@ function buildTerms(terms) { } async function loadPreview(component, templateId) { + const existingPreview = component.querySelector('.terms-conditions-preview'); + + if (existingPreview) return; + let host; if (window.location.href.includes('.hlx.')) { host = window.location.origin.replace(window.location.hostname, `${ECC_ENV}--events-milo--adobecom.hlx.page`); @@ -39,23 +43,24 @@ async function loadPreview(component, templateId) { } const rsvpFormLocation = `${host}${templateId.substring(0, templateId.lastIndexOf('/'))}/rsvp-form`; - const text = await fetchThrottledMemoizedText(`${rsvpFormLocation}.plain.html`).catch(() => ({})) - .catch(() => ({})); + const resp = await fetchThrottledMemoizedText(`${rsvpFormLocation}.plain.html`); - if (!text) { + if (!resp) { component.remove(); return; } - const doc = new DOMParser().parseFromString(text, 'text/html'); - const termsConditionsRow = doc.querySelector('.events-form > div:nth-of-type(3)'); + if (typeof resp === 'string') { + const doc = new DOMParser().parseFromString(resp, 'text/html'); + const termsConditionsRow = doc.querySelector('.events-form > div:nth-of-type(3)'); - if (!termsConditionsRow) { - component.remove(); - return; - } + if (!termsConditionsRow) { + component.remove(); + return; + } - component.append(buildTerms(termsConditionsRow)); + component.append(buildTerms(termsConditionsRow)); + } } export async function onUpdate(component, props) { diff --git a/ecc/scripts/esp-controller.js b/ecc/scripts/esp-controller.js index 3c562e9b..5cc7e759 100644 --- a/ecc/scripts/esp-controller.js +++ b/ecc/scripts/esp-controller.js @@ -549,7 +549,7 @@ export async function updateEvent(eventId, payload) { export async function publishEvent(eventId, payload) { const { host } = getAPIConfig().esp[ECC_ENV]; - const raw = JSON.stringify({ ...payload, published: true }); + const raw = JSON.stringify({ ...payload, published: true, liveUpdate: true }); const options = await constructRequestOptions('PUT', raw); try { @@ -570,7 +570,7 @@ export async function publishEvent(eventId, payload) { export async function unpublishEvent(eventId, payload) { const { host } = getAPIConfig().esp[ECC_ENV]; - const raw = JSON.stringify({ ...payload, published: false }); + const raw = JSON.stringify({ ...payload, published: false, liveUpdate: true }); const options = await constructRequestOptions('PUT', raw); try { From ee49777aefc7fe65c8db919ed4dde5c5018f6083 Mon Sep 17 00:00:00 2001 From: Qiyun Dai Date: Mon, 19 Aug 2024 16:40:17 -0500 Subject: [PATCH 3/7] status tag added (#183) From 6db18ead205c21d06b944f0d79b37e89782d347c Mon Sep 17 00:00:00 2001 From: Qiyun Dai Date: Mon, 19 Aug 2024 16:40:37 -0500 Subject: [PATCH 4/7] RSVP related fixes (#180) --- ...gistration-details-component-controller.js | 68 ++++++++++--------- ...egistration-fields-component-controller.js | 2 +- ecc/blocks/form-handler/form-handler.js | 14 ++++ .../registration-fields-component.js | 10 ++- 4 files changed, 60 insertions(+), 34 deletions(-) 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 780d21e7..39f11415 100644 --- a/ecc/blocks/form-handler/controllers/registration-details-component-controller.js +++ b/ecc/blocks/form-handler/controllers/registration-details-component-controller.js @@ -1,35 +1,5 @@ /* eslint-disable no-unused-vars */ -export function onSubmit(component, props) { - if (component.closest('.fragment')?.classList.contains('hidden')) return; - - const attendeeLimitVal = component.querySelector('#attendee-count-input')?.value; - 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 rsvpDescription = component.querySelector('#rsvp-form-detail-description')?.value; - - const attendeeLimit = Number.isNaN(+attendeeLimitVal) ? null : +attendeeLimitVal; - - const rsvpData = {}; - - if (rsvpDescription) rsvpData.rsvpDescription = rsvpDescription; - if (contactHost && hostEmail) rsvpData.hostEmail = hostEmail; - if (attendeeLimit) rsvpData.attendeeLimit = attendeeLimit; - if (allowWaitlisting) rsvpData.allowWaitlisting = allowWaitlisting; - - props.payload = { ...props.payload, ...rsvpData }; -} - -export async function onUpdate(component, props) { - if (!props.eventDataResp) return; - - if (props.eventDataResp.cloudType === 'CreativeCloud') { - component.querySelector('.attendee-count-wrapper')?.classList.add('hidden'); - component.querySelector('#registration-allow-waitlist')?.classList.add('hidden'); - } -} - -export default function init(component, props) { +function prefillFields(component, props) { const contactHostEl = component.querySelector('#registration-contact-host'); const hostEmailEl = component.querySelector('#event-host-email-input'); const attendeeLimitEl = component.querySelector('#attendee-count-input'); @@ -66,3 +36,39 @@ export default function init(component, props) { }); } } + +export function onSubmit(component, props) { + if (component.closest('.fragment')?.classList.contains('hidden')) return; + + const attendeeLimitVal = component.querySelector('#attendee-count-input')?.value; + 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 rsvpDescription = component.querySelector('#rsvp-form-detail-description')?.value; + + const attendeeLimit = Number.isNaN(+attendeeLimitVal) ? null : +attendeeLimitVal; + + const rsvpData = {}; + + if (rsvpDescription) rsvpData.rsvpDescription = rsvpDescription; + if (contactHost && hostEmail) rsvpData.hostEmail = hostEmail; + if (attendeeLimit) rsvpData.attendeeLimit = attendeeLimit; + if (allowWaitlisting) rsvpData.allowWaitlisting = allowWaitlisting; + + props.payload = { ...props.payload, ...rsvpData }; +} + +export async function onUpdate(component, props) { + if (!props.eventDataResp) return; + + if (props.eventDataResp.cloudType === 'CreativeCloud') { + component.querySelector('.attendee-count-wrapper')?.classList.add('hidden'); + component.querySelector('#registration-allow-waitlist')?.classList.add('hidden'); + } + + prefillFields(component, props); +} + +export default function init(component, props) { + prefillFields(component, props); +} diff --git a/ecc/blocks/form-handler/controllers/registration-fields-component-controller.js b/ecc/blocks/form-handler/controllers/registration-fields-component-controller.js index 27425fb0..437d2440 100644 --- a/ecc/blocks/form-handler/controllers/registration-fields-component-controller.js +++ b/ecc/blocks/form-handler/controllers/registration-fields-component-controller.js @@ -2,7 +2,7 @@ export function onSubmit(component, props) { if (component.closest('.fragment')?.classList.contains('hidden')) return; - const defaultFields = ['firstName', 'lastName', 'email', 'jobTitle']; + const defaultFields = component.dataset.mandatedfields?.split(','); const rsvpFormFields = { visible: [...defaultFields, ...Array.from(component.querySelectorAll('sp-checkbox.check-appear[checked]')).map((f) => f.name)], diff --git a/ecc/blocks/form-handler/form-handler.js b/ecc/blocks/form-handler/form-handler.js index e329482e..47436534 100644 --- a/ecc/blocks/form-handler/form-handler.js +++ b/ecc/blocks/form-handler/form-handler.js @@ -608,6 +608,20 @@ function updateCtas(props) { a.classList.remove('preview-not-ready'); } } + + if (a.classList.contains('next-button')) { + if (props.currentStep === props.maxStep) { + if (props.eventDataResp.published) { + a.textContent = a.dataset.republishStateText; + } else { + a.textContent = a.dataset.finalStateText; + } + a.prepend(getIcon('golden-rocket')); + } else { + a.textContent = a.dataset.nextStateText; + a.append(getIcon('chev-right-white')); + } + } }); } diff --git a/ecc/blocks/registration-fields-component/registration-fields-component.js b/ecc/blocks/registration-fields-component/registration-fields-component.js index 18eb4599..8abe7554 100644 --- a/ecc/blocks/registration-fields-component/registration-fields-component.js +++ b/ecc/blocks/registration-fields-component/registration-fields-component.js @@ -10,7 +10,11 @@ function convertString(input) { return result; } -async function decorateRSVPFields(row) { +async function decorateRSVPFields(el) { + const row = el.querySelector(':scope > div:last-of-type'); + + if (!row) return; + row.classList.add('rsvp-checkboxes'); const configSheetLocation = row.querySelector('a')?.href; const config = await fetch(configSheetLocation) @@ -30,6 +34,8 @@ async function decorateRSVPFields(row) { row.innerHTML = ''; row.append(fieldConfigTable); + el.dataset.mandatedfields = config.data.filter((f) => f.Required === 'x').map((f) => f.Field); + config.data.filter((f) => f.Required !== 'x' && f.Type !== 'submit').forEach((field) => { const fieldRow = createTag('tr', { class: 'field-row' }, '', { parent: tbody }); const tds = []; @@ -46,5 +52,5 @@ async function decorateRSVPFields(row) { export default async function init(el) { el.classList.add('form-component'); generateToolTip(el.querySelector(':scope > div:first-of-type')); - await decorateRSVPFields(el.querySelector(':scope > div:last-of-type')); + await decorateRSVPFields(el); } From 0f9e412eb568af214daf5da458617d371101ebc9 Mon Sep 17 00:00:00 2001 From: Qiyun Dai Date: Tue, 20 Aug 2024 18:36:55 -0500 Subject: [PATCH 5/7] update sorting mechanism (#186) --- ecc/blocks/ecc-dashboard/ecc-dashboard.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/ecc/blocks/ecc-dashboard/ecc-dashboard.js b/ecc/blocks/ecc-dashboard/ecc-dashboard.js index c8244530..d2a70d6b 100644 --- a/ecc/blocks/ecc-dashboard/ecc-dashboard.js +++ b/ecc/blocks/ecc-dashboard/ecc-dashboard.js @@ -209,7 +209,7 @@ function sortData(props, config, options = {}) { let valA; let valB; - if ((field === 'title' || field === 'venueId')) { + if ((field === 'title')) { valA = a[field]?.toLowerCase() || ''; valB = b[field]?.toLowerCase() || ''; return sortAscending ? valA.localeCompare(valB) : valB.localeCompare(valA); @@ -221,6 +221,18 @@ function sortData(props, config, options = {}) { return sortAscending ? valA - valB : valB - valA; } + if (field === 'venueName') { + valA = a.venue?.venueName?.toLowerCase() || ''; + valB = b.venue?.venueName?.toLowerCase() || ''; + return sortAscending ? valA.localeCompare(valB) : valB.localeCompare(valA); + } + + if (typeof a[field] === typeof b[field] && typeof a[field] === 'number') { + valA = a[field] || 0; + valB = b[field] || 0; + return sortAscending ? valA - valB : valB - valA; + } + valA = a[field]?.toString().toLowerCase() || ''; valB = b[field]?.toString().toLowerCase() || ''; return sortAscending ? valA.localeCompare(valB) : valB.localeCompare(valA); @@ -538,9 +550,9 @@ function initSorting(props, config) { published: 'PUBLISH STATUS', startDate: 'DATE RUN', modificationTime: 'LAST MODIFIED', - venueId: 'VENUE NAME', + venueName: 'VENUE NAME', timezone: 'GEO', - externalEventId: 'RSVP DATA', + attendeeCount: 'RSVP DATA', manage: 'MANAGE', }; From a3206a717fd4a23cb09f8322e67433d359d46509 Mon Sep 17 00:00:00 2001 From: Qiyun Dai Date: Tue, 20 Aug 2024 18:37:24 -0500 Subject: [PATCH 6/7] product selector now syncs with topics removal (#185) --- .../product-promotion-component-controller.js | 33 +++++++++++++------ .../product-selector-group.js | 3 +- .../profile-container.css.js | 1 + 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/ecc/blocks/form-handler/controllers/product-promotion-component-controller.js b/ecc/blocks/form-handler/controllers/product-promotion-component-controller.js index 42b7b24b..4e415a1d 100644 --- a/ecc/blocks/form-handler/controllers/product-promotion-component-controller.js +++ b/ecc/blocks/form-handler/controllers/product-promotion-component-controller.js @@ -10,11 +10,14 @@ export function onSubmit(component, props) { const selectedProducts = productGroup?.getSelectedProducts(); if (selectedProducts) { - const relatedProducts = selectedProducts.map((p) => ({ - name: p.title, - showProductBlade: !!p.showProductBlade, - tags: p.tags.map((t) => t.tagID).join(','), - })); + const topicsVal = props.payload.fullTopicsValue?.map((x) => JSON.parse(x)); + const relatedProducts = selectedProducts + .filter((p) => topicsVal.find((t) => p.tagID.includes(t.tagID))) + .map((p) => ({ + name: p.title, + showProductBlade: !!p.showProductBlade, + tags: p.tags.map((t) => t.tagID).join(','), + })); props.payload = { ...props.payload, relatedProducts }; } @@ -68,12 +71,22 @@ async function updateProductSelector(component, props) { (p) => topicsVal.find((t) => p.tagID.includes(t.tagID)) && supportedProducts.includes(p.title), ); - productGroups.forEach((p) => { - p.setAttribute('data-products', JSON.stringify(products)); - p.setAttribute('data-selected-topics', JSON.stringify(topicsVal)); - p.requestUpdate(); + productGroups.forEach((pg) => { + pg.setAttribute('data-products', JSON.stringify(products)); + pg.setAttribute('data-selected-topics', JSON.stringify(topicsVal)); + pg.requestUpdate(); + + const selectedProducts = pg.getSelectedProducts(); + + selectedProducts.forEach((sp, i) => { + const isProductAvailable = products.find((p) => p.name === sp.name); + + if (!isProductAvailable) { + pg.deleteProduct(i); + } + }); - p.shadowRoot.querySelectorAll('product-selector').forEach((ps) => { + pg.shadowRoot.querySelectorAll('product-selector').forEach((ps) => { ps.dispatchEvent(new CustomEvent('update-product', { detail: { product: ps.selectedProduct }, bubbles: true, diff --git a/ecc/components/product-selector-group/product-selector-group.js b/ecc/components/product-selector-group/product-selector-group.js index 00e2f058..f6acff37 100644 --- a/ecc/components/product-selector-group/product-selector-group.js +++ b/ecc/components/product-selector-group/product-selector-group.js @@ -103,7 +103,8 @@ export default class ProductSelectorGroup extends LitElement { `)} - + ${this.selectedProducts.length < uniqueProducts.length ? html`` : nothing} + `; } } diff --git a/ecc/components/profile-container/profile-container.css.js b/ecc/components/profile-container/profile-container.css.js index 8a90abe6..05cbb6fb 100644 --- a/ecc/components/profile-container/profile-container.css.js +++ b/ecc/components/profile-container/profile-container.css.js @@ -17,6 +17,7 @@ export const style = css` margin: 24px; box-shadow: 0 3px 6px 0 rgb(0 0 0 / 16%); display: flex; + background-color: var(--color-white); } profile-ui { From 34606a6a26a86693f3e2b12c2ed2a587e19992ca Mon Sep 17 00:00:00 2001 From: Qiyun Dai Date: Tue, 20 Aug 2024 18:37:40 -0500 Subject: [PATCH 7/7] Update form-handler.css (#184) --- ecc/blocks/form-handler/form-handler.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ecc/blocks/form-handler/form-handler.css b/ecc/blocks/form-handler/form-handler.css index 4fd12f21..14f230c9 100644 --- a/ecc/blocks/form-handler/form-handler.css +++ b/ecc/blocks/form-handler/form-handler.css @@ -234,8 +234,14 @@ cursor: pointer; } +.form-handler .side-menu .nav-item:disabled { + pointer-events: none; + cursor: unset; +} + .form-handler .side-menu .nav-item.disabled { pointer-events: none; + cursor: unset; opacity: 0.5; }