From 0385643f99d64ae6826339938c6cb3d183a1a39d Mon Sep 17 00:00:00 2001 From: Qiyun Dai Date: Fri, 30 Aug 2024 20:27:18 -0500 Subject: [PATCH 01/10] event-info duplication prevention added --- .../event-info-component.css | 9 ++++++++ .../event-info-component.js | 2 +- .../event-format-component-controller.js | 9 ++++++-- .../event-info-component-controller.js | 23 ++++++++++++++++++- ecc/scripts/utils.js | 6 ++++- 5 files changed, 44 insertions(+), 5 deletions(-) diff --git a/ecc/blocks/event-info-component/event-info-component.css b/ecc/blocks/event-info-component/event-info-component.css index 413e2229..9d326171 100644 --- a/ecc/blocks/event-info-component/event-info-component.css +++ b/ecc/blocks/event-info-component/event-info-component.css @@ -11,6 +11,15 @@ width: 100%; } +.event-info-component sp-textfield#info-field-event-title sp-help-text { + position: absolute; + display: none; +} + +.event-info-component sp-textfield#info-field-event-title.show-negative-help-text sp-help-text { + display: flex; +} + .event-info-component sp-textfield.textarea-input { font-size: var(--type-body-m-size);; width: 100%; diff --git a/ecc/blocks/event-info-component/event-info-component.js b/ecc/blocks/event-info-component/event-info-component.js index a938ee97..3197c4ae 100644 --- a/ecc/blocks/event-info-component/event-info-component.js +++ b/ecc/blocks/event-info-component/event-info-component.js @@ -94,7 +94,7 @@ export default function init(el) { generateToolTip(r); break; case 1: - await decorateTextfield(r, { id: 'info-field-event-title' }); + await decorateTextfield(r, { id: 'info-field-event-title' }, 'This event title is already taken'); break; case 2: await decorateTextarea(r, { id: 'info-field-event-description', grows: true, quiet: true }); diff --git a/ecc/blocks/form-handler/controllers/event-format-component-controller.js b/ecc/blocks/form-handler/controllers/event-format-component-controller.js index d0f0951e..9b3ac1fc 100644 --- a/ecc/blocks/form-handler/controllers/event-format-component-controller.js +++ b/ecc/blocks/form-handler/controllers/event-format-component-controller.js @@ -67,7 +67,7 @@ export async function onUpdate(component, props) { } } -async function populateSeriesOptions(component) { +async function populateSeriesOptions(props, component) { const seriesSelect = component.querySelector('#series-select-input'); if (!seriesSelect) return; @@ -84,6 +84,11 @@ async function populateSeriesOptions(component) { }); seriesSelect.pending = false; + + seriesSelect.addEventListener('change', () => { + const seriesId = seriesSelect.value; + props.payload = { ...props.payload, ...{ seriesId } }; + }); } export default async function init(component, props) { @@ -104,7 +109,7 @@ export default async function init(component, props) { const eventData = props.eventDataResp; prepopulateTimeZone(component); initStepLock(component); - await populateSeriesOptions(component); + await populateSeriesOptions(props, component); const { cloudType, diff --git a/ecc/blocks/form-handler/controllers/event-info-component-controller.js b/ecc/blocks/form-handler/controllers/event-info-component-controller.js index 4b6e042c..27c655b7 100644 --- a/ecc/blocks/form-handler/controllers/event-info-component-controller.js +++ b/ecc/blocks/form-handler/controllers/event-info-component-controller.js @@ -1,5 +1,6 @@ /* eslint-disable no-unused-vars */ /* eslint-disable no-use-before-define */ +import { getEvents } from '../../../scripts/esp-controller.js'; import { LIBS } from '../../../scripts/scripts.js'; import { changeInputValue } from '../../../scripts/utils.js'; @@ -345,14 +346,34 @@ export async function onUpdate(component, props) { // do nothing } -export default function init(component, props) { +export default async function init(component, props) { + const allEventsResp = await getEvents(); + const allEvents = allEventsResp?.events; const eventData = props.eventDataResp; + + const eventTitleInput = component.querySelector('#info-field-event-title'); const startTimeInput = component.querySelector('#time-picker-start-time'); const endTimeInput = component.querySelector('#time-picker-end-time'); const datePicker = component.querySelector('#event-info-date-picker'); initCalendar(component); + eventTitleInput.addEventListener('change', () => { + const sameSeriesEvents = allEvents?.filter((e) => { + const matchInPayload = e.seriesId === props.payload.seriesId; + const matchInResp = e.seriesId === eventData.seriesId; + return matchInPayload || matchInResp; + }) || []; + + if (sameSeriesEvents.some((event) => event.title === eventTitleInput.value)) { + eventTitleInput.classList.add('show-negative-help-text'); + eventTitleInput.invalid = true; + } else { + eventTitleInput.classList.remove('show-negative-help-text'); + eventTitleInput.invalid = false; + } + }); + endTimeInput.addEventListener('change', () => { if (datePicker.dataset.startDate !== datePicker.dataset.endDate) return; const allOptions = startTimeInput.querySelectorAll('sp-menu-item'); diff --git a/ecc/scripts/utils.js b/ecc/scripts/utils.js index 5ba58ece..adeffa38 100644 --- a/ecc/scripts/utils.js +++ b/ecc/scripts/utils.js @@ -125,7 +125,7 @@ function mergeOptions(defaultOptions, overrideOptions) { return combinedOptions; } -export async function decorateTextfield(cell, extraOptions) { +export async function decorateTextfield(cell, extraOptions, negativeHelperText = '') { cell.classList.add('text-field-row'); const cols = cell.querySelectorAll(':scope > div'); if (!cols.length) return; @@ -153,6 +153,10 @@ export async function decorateTextfield(cell, extraOptions) { extraOptions, )); + if (negativeHelperText) { + createTag('sp-help-text', { variant: 'negative', slot: 'negative-help-text', icon: true }, negativeHelperText, { parent: input }); + } + if (maxCharNum) input.setAttribute('maxlength', maxCharNum); const wrapper = createTag('div', { class: 'info-field-wrapper' }); From 7553ce660bd1dc32f3b2785bb10174931e3fc9da Mon Sep 17 00:00:00 2001 From: Qiyun Dai Date: Fri, 30 Aug 2024 20:51:20 -0500 Subject: [PATCH 02/10] use placeholders --- .../event-info-component.js | 11 +++++++++-- ecc/scripts/utils.js | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/ecc/blocks/event-info-component/event-info-component.js b/ecc/blocks/event-info-component/event-info-component.js index 3197c4ae..41db6aaf 100644 --- a/ecc/blocks/event-info-component/event-info-component.js +++ b/ecc/blocks/event-info-component/event-info-component.js @@ -1,5 +1,12 @@ import { LIBS } from '../../scripts/scripts.js'; -import { getIcon, generateToolTip, decorateTextfield, decorateTextarea, convertTo24HourFormat } from '../../scripts/utils.js'; +import { + getIcon, + generateToolTip, + decorateTextfield, + decorateTextarea, + convertTo24HourFormat, + miloReplaceKey, +} from '../../scripts/utils.js'; const { createTag } = await import(`${LIBS}/utils/utils.js`); @@ -94,7 +101,7 @@ export default function init(el) { generateToolTip(r); break; case 1: - await decorateTextfield(r, { id: 'info-field-event-title' }, 'This event title is already taken'); + await decorateTextfield(r, { id: 'info-field-event-title' }, await miloReplaceKey('duplicate-event-title-error')); break; case 2: await decorateTextarea(r, { id: 'info-field-event-description', grows: true, quiet: true }); diff --git a/ecc/scripts/utils.js b/ecc/scripts/utils.js index adeffa38..e5a05276 100644 --- a/ecc/scripts/utils.js +++ b/ecc/scripts/utils.js @@ -230,6 +230,24 @@ export function getServiceName(link) { return url.hostname.replace('.com', '').replace('www.', ''); } +export async function miloReplaceKey(key) { + try { + const [utils, placeholders] = await Promise.all([ + import(`${LIBS}/utils/utils.js`), + import(`${LIBS}/features/placeholders.js`), + ]); + + const { getConfig } = utils; + const { replaceKey } = placeholders; + const config = getConfig(); + + return await replaceKey(key, config); + } catch (error) { + window.lana?.log('Error trying to replace placeholder:', error); + return 'RSVP'; + } +} + export const fetchThrottledMemoizedText = (() => { const cache = new Map(); const pending = new Map(); From ce797d7c0c7484adfe57e70574cbd156ae578129 Mon Sep 17 00:00:00 2001 From: Qiyun Dai Date: Tue, 3 Sep 2024 14:58:39 -0500 Subject: [PATCH 03/10] WIP --- .../event-info-component-controller.js | 22 ++++++++++++++++++- .../venue-info-component-controller.js | 5 +++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/ecc/blocks/form-handler/controllers/event-info-component-controller.js b/ecc/blocks/form-handler/controllers/event-info-component-controller.js index 27c655b7..b0180947 100644 --- a/ecc/blocks/form-handler/controllers/event-info-component-controller.js +++ b/ecc/blocks/form-handler/controllers/event-info-component-controller.js @@ -346,6 +346,15 @@ export async function onUpdate(component, props) { // do nothing } +function checkEventDuplication(event, currentEvent) { + const titleMatch = event.title === currentEvent.title; + const startDateMatch = event.localStartDate === currentEvent.localStartDate; + const venueIdMatch = event.venue?.placeId === currentEvent.venuePlaceId; + const eventIdNoMatch = event.eventId !== currentEvent.eventId; + + return titleMatch && startDateMatch && venueIdMatch && eventIdNoMatch; +} + export default async function init(component, props) { const allEventsResp = await getEvents(); const allEvents = allEventsResp?.events; @@ -365,7 +374,12 @@ export default async function init(component, props) { return matchInPayload || matchInResp; }) || []; - if (sameSeriesEvents.some((event) => event.title === eventTitleInput.value)) { + if (sameSeriesEvents.some((event) => checkEventDuplication(event, { + title: eventTitleInput.value, + localStartDate: datePicker.dataset.startDate, + venuePlaceId: props.payload.venuePlaceId || eventData.venue?.placeId, + eventId: eventData.eventId, + }))) { eventTitleInput.classList.add('show-negative-help-text'); eventTitleInput.invalid = true; } else { @@ -407,6 +421,12 @@ export default async function init(component, props) { option.disabled = false; }); } + + props.payload = { + ...props.payload, + localStartDate: datePicker.dataset.startDate, + localEndDate: datePicker.dataset.endDate, + }; }); const { 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 6d92b1d9..a4880c40 100644 --- a/ecc/blocks/form-handler/controllers/venue-info-component-controller.js +++ b/ecc/blocks/form-handler/controllers/venue-info-component-controller.js @@ -29,7 +29,7 @@ async function loadGoogleMapsAPI(callback) { document.head.appendChild(script); } -function initAutocomplete(el) { +function initAutocomplete(el, props) { const venueName = el.querySelector('#venue-info-venue-name'); // eslint-disable-next-line no-undef if (!google) return; @@ -97,6 +97,7 @@ function initAutocomplete(el) { changeInputValue(mapUrl, 'value', place.url); togglePrefillableFieldsHiddenState(el, false); + props.payload = { ...props.payload, venuePlaceId: place.place_id }; } if (place.geometry) { @@ -118,7 +119,7 @@ export async function onUpdate(component, props) { export default async function init(component, props) { const eventData = props.eventDataResp; - await loadGoogleMapsAPI(() => initAutocomplete(component)); + await loadGoogleMapsAPI(() => initAutocomplete(component, props)); const { venue, showVenuePostEvent } = eventData; From 5983920ae3ecfb1f47df7dc5993f1c1631c0e1a1 Mon Sep 17 00:00:00 2001 From: Qiyun Dai Date: Tue, 3 Sep 2024 15:05:50 -0500 Subject: [PATCH 04/10] validation logic update --- ecc/blocks/form-handler/form-handler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ecc/blocks/form-handler/form-handler.js b/ecc/blocks/form-handler/form-handler.js index 47436534..86a4e8db 100644 --- a/ecc/blocks/form-handler/form-handler.js +++ b/ecc/blocks/form-handler/form-handler.js @@ -151,7 +151,7 @@ function getCurrentFragment(props) { } function validateRequiredFields(fields) { - return fields.length === 0 || Array.from(fields).every((f) => f.value); + return fields.length === 0 || Array.from(fields).every((f) => f.value && !f.invalid); } function onStepValidate(props) { From c9c128e02b046c41f50286954f45160f645ec3c3 Mon Sep 17 00:00:00 2001 From: Qiyun Dai Date: Tue, 3 Sep 2024 15:05:55 -0500 Subject: [PATCH 05/10] adding toast --- .../controllers/event-info-component-controller.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ecc/blocks/form-handler/controllers/event-info-component-controller.js b/ecc/blocks/form-handler/controllers/event-info-component-controller.js index b0180947..cef662d3 100644 --- a/ecc/blocks/form-handler/controllers/event-info-component-controller.js +++ b/ecc/blocks/form-handler/controllers/event-info-component-controller.js @@ -2,7 +2,7 @@ /* eslint-disable no-use-before-define */ import { getEvents } from '../../../scripts/esp-controller.js'; import { LIBS } from '../../../scripts/scripts.js'; -import { changeInputValue } from '../../../scripts/utils.js'; +import { changeInputValue, miloReplaceKey } from '../../../scripts/utils.js'; const { createTag, getConfig } = await import(`${LIBS}/utils/utils.js`); @@ -357,6 +357,7 @@ function checkEventDuplication(event, currentEvent) { export default async function init(component, props) { const allEventsResp = await getEvents(); + const dupEventError = await miloReplaceKey('duplicate-event-title-error'); const allEvents = allEventsResp?.events; const eventData = props.eventDataResp; @@ -382,6 +383,12 @@ export default async function init(component, props) { }))) { eventTitleInput.classList.add('show-negative-help-text'); eventTitleInput.invalid = true; + + const toastArea = props.el.querySelector('.toast-area'); + const toast = createTag('sp-toast', { open: true, variant: 'negative', timeout: 6000 }, dupEventError, { parent: toastArea }); + toast.addEventListener('close', () => { + toast.remove(); + }); } else { eventTitleInput.classList.remove('show-negative-help-text'); eventTitleInput.invalid = false; From 030c7c4067f7d6599d719b24b19d56becc2f4063 Mon Sep 17 00:00:00 2001 From: Qiyun Dai Date: Wed, 4 Sep 2024 09:08:21 -0500 Subject: [PATCH 06/10] wip --- .../event-format-component-controller.js | 4 +- .../event-info-component-controller.js | 50 ++++++++----------- .../venue-info-component-controller.js | 5 +- ecc/blocks/form-handler/form-handler.css | 7 +++ 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/ecc/blocks/form-handler/controllers/event-format-component-controller.js b/ecc/blocks/form-handler/controllers/event-format-component-controller.js index 9b3ac1fc..35be7960 100644 --- a/ecc/blocks/form-handler/controllers/event-format-component-controller.js +++ b/ecc/blocks/form-handler/controllers/event-format-component-controller.js @@ -1,6 +1,6 @@ /* eslint-disable no-unused-vars */ import { getSeries } from '../../../scripts/esp-controller.js'; -import { MILO_CONFIG, LIBS } from '../../../scripts/scripts.js'; +import { MILO_CONFIG, LIBS, BlockMediator } from '../../../scripts/scripts.js'; import { changeInputValue } from '../../../scripts/utils.js'; const { createTag } = await import(`${LIBS}/utils/utils.js`); @@ -87,7 +87,7 @@ async function populateSeriesOptions(props, component) { seriesSelect.addEventListener('change', () => { const seriesId = seriesSelect.value; - props.payload = { ...props.payload, ...{ seriesId } }; + BlockMediator.set('eventDupMetrics', { ...BlockMediator.get('eventDupMetrics'), seriesId }); }); } diff --git a/ecc/blocks/form-handler/controllers/event-info-component-controller.js b/ecc/blocks/form-handler/controllers/event-info-component-controller.js index cef662d3..6c599992 100644 --- a/ecc/blocks/form-handler/controllers/event-info-component-controller.js +++ b/ecc/blocks/form-handler/controllers/event-info-component-controller.js @@ -1,7 +1,7 @@ /* eslint-disable no-unused-vars */ /* eslint-disable no-use-before-define */ import { getEvents } from '../../../scripts/esp-controller.js'; -import { LIBS } from '../../../scripts/scripts.js'; +import { BlockMediator, LIBS } from '../../../scripts/scripts.js'; import { changeInputValue, miloReplaceKey } from '../../../scripts/utils.js'; const { createTag, getConfig } = await import(`${LIBS}/utils/utils.js`); @@ -346,11 +346,11 @@ export async function onUpdate(component, props) { // do nothing } -function checkEventDuplication(event, currentEvent) { - const titleMatch = event.title === currentEvent.title; - const startDateMatch = event.localStartDate === currentEvent.localStartDate; - const venueIdMatch = event.venue?.placeId === currentEvent.venuePlaceId; - const eventIdNoMatch = event.eventId !== currentEvent.eventId; +function checkEventDuplication(event, compareMetrics) { + const titleMatch = event.title === compareMetrics.title; + const startDateMatch = event.localStartDate === compareMetrics.startDate; + const venueIdMatch = event.venue?.city === compareMetrics.city; + const eventIdNoMatch = event.eventId !== compareMetrics.eventId; return titleMatch && startDateMatch && venueIdMatch && eventIdNoMatch; } @@ -375,24 +375,7 @@ export default async function init(component, props) { return matchInPayload || matchInResp; }) || []; - if (sameSeriesEvents.some((event) => checkEventDuplication(event, { - title: eventTitleInput.value, - localStartDate: datePicker.dataset.startDate, - venuePlaceId: props.payload.venuePlaceId || eventData.venue?.placeId, - eventId: eventData.eventId, - }))) { - eventTitleInput.classList.add('show-negative-help-text'); - eventTitleInput.invalid = true; - - const toastArea = props.el.querySelector('.toast-area'); - const toast = createTag('sp-toast', { open: true, variant: 'negative', timeout: 6000 }, dupEventError, { parent: toastArea }); - toast.addEventListener('close', () => { - toast.remove(); - }); - } else { - eventTitleInput.classList.remove('show-negative-help-text'); - eventTitleInput.invalid = false; - } + BlockMediator.set('eventDupMetrics', { ...BlockMediator.get('eventDupMetrics'), title: eventTitleInput.value }); }); endTimeInput.addEventListener('change', () => { @@ -429,11 +412,20 @@ export default async function init(component, props) { }); } - props.payload = { - ...props.payload, - localStartDate: datePicker.dataset.startDate, - localEndDate: datePicker.dataset.endDate, - }; + BlockMediator.set('eventDupMetrics', { ...BlockMediator.get('eventDupMetrics'), startDate: datePicker.dataset.startDate }); + }); + + BlockMediator.subscribe('eventDupMetrics', (store) => { + const metrics = store.newValue; + + const isDup = allEvents?.some((e) => checkEventDuplication(e, metrics)); + if (isDup) { + props.el.classList.add('show-dup-event-error'); + eventTitleInput.invalid = true; + } else { + props.el.classList.remove('show-dup-event-error'); + eventTitleInput.invalid = false; + } }); const { 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 a4880c40..55ac0ddc 100644 --- a/ecc/blocks/form-handler/controllers/venue-info-component-controller.js +++ b/ecc/blocks/form-handler/controllers/venue-info-component-controller.js @@ -1,6 +1,6 @@ /* eslint-disable no-unused-vars */ import { createVenue, replaceVenue } from '../../../scripts/esp-controller.js'; -import { ECC_ENV } from '../../../scripts/scripts.js'; +import { BlockMediator, ECC_ENV } from '../../../scripts/scripts.js'; import { changeInputValue, getSecret } from '../../../scripts/utils.js'; import { buildErrorMessage } from '../form-handler.js'; @@ -97,7 +97,7 @@ function initAutocomplete(el, props) { changeInputValue(mapUrl, 'value', place.url); togglePrefillableFieldsHiddenState(el, false); - props.payload = { ...props.payload, venuePlaceId: place.place_id }; + BlockMediator.set('eventDupMetrics', { ...BlockMediator.get('eventDupMetrics'), city: addressInfo.city }); } if (place.geometry) { @@ -179,6 +179,7 @@ export default async function init(component, props) { changeInputValue(placeIdInput, 'value', placeId); changeInputValue(mapUrlInput, 'value', mapUrl); changeInputValue(gmtoffsetInput, 'value', venue.gmtOffset); + BlockMediator.set('eventDupMetrics', { ...BlockMediator.get('eventDupMetrics'), placeId }); if (venueName) { component.classList.add('prefilled'); diff --git a/ecc/blocks/form-handler/form-handler.css b/ecc/blocks/form-handler/form-handler.css index 14f230c9..479646f5 100644 --- a/ecc/blocks/form-handler/form-handler.css +++ b/ecc/blocks/form-handler/form-handler.css @@ -76,6 +76,13 @@ .form-handler.show-error { --mod-textfield-icon-size-invalid: 16px; + --mod-textfield-border-color-invalid-default: unset; +} + +.form-handler.show-dup-event-error #info-field-event-title { + --mod-textfield-icon-size-invalid: 16px; + --mod-textfield-border-color-invalid-default: unset; + } .form-handler .main-frame { From 77d71a71b32f8e3a1cea564175d7b3d2e2a8df8c Mon Sep 17 00:00:00 2001 From: Qiyun Dai Date: Wed, 4 Sep 2024 10:46:25 -0500 Subject: [PATCH 07/10] prototype working --- .../event-info-component.css | 2 +- .../event-info-component-controller.js | 27 ++++++++++++------- .../venue-info-component-controller.js | 2 +- ecc/blocks/form-handler/form-handler.css | 6 +++-- ecc/blocks/form-handler/form-handler.js | 1 + 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/ecc/blocks/event-info-component/event-info-component.css b/ecc/blocks/event-info-component/event-info-component.css index 9d326171..a76ab279 100644 --- a/ecc/blocks/event-info-component/event-info-component.css +++ b/ecc/blocks/event-info-component/event-info-component.css @@ -42,7 +42,7 @@ .event-info-component .date-time-row > .date-picker { position: relative; border-bottom: 1px solid var(--color-black); - min-width: 280px; + min-width: 300px; display: flex; align-items: center; gap: 8px; diff --git a/ecc/blocks/form-handler/controllers/event-info-component-controller.js b/ecc/blocks/form-handler/controllers/event-info-component-controller.js index 6c599992..740caf45 100644 --- a/ecc/blocks/form-handler/controllers/event-info-component-controller.js +++ b/ecc/blocks/form-handler/controllers/event-info-component-controller.js @@ -357,9 +357,13 @@ function checkEventDuplication(event, compareMetrics) { export default async function init(component, props) { const allEventsResp = await getEvents(); - const dupEventError = await miloReplaceKey('duplicate-event-title-error'); const allEvents = allEventsResp?.events; const eventData = props.eventDataResp; + const sameSeriesEvents = allEvents?.filter((e) => { + const matchInPayload = e.seriesId === props.payload.seriesId; + const matchInResp = e.seriesId === eventData.seriesId; + return matchInPayload || matchInResp; + }) || []; const eventTitleInput = component.querySelector('#info-field-event-title'); const startTimeInput = component.querySelector('#time-picker-start-time'); @@ -368,13 +372,7 @@ export default async function init(component, props) { initCalendar(component); - eventTitleInput.addEventListener('change', () => { - const sameSeriesEvents = allEvents?.filter((e) => { - const matchInPayload = e.seriesId === props.payload.seriesId; - const matchInResp = e.seriesId === eventData.seriesId; - return matchInPayload || matchInResp; - }) || []; - + eventTitleInput.addEventListener('input', () => { BlockMediator.set('eventDupMetrics', { ...BlockMediator.get('eventDupMetrics'), title: eventTitleInput.value }); }); @@ -418,7 +416,7 @@ export default async function init(component, props) { BlockMediator.subscribe('eventDupMetrics', (store) => { const metrics = store.newValue; - const isDup = allEvents?.some((e) => checkEventDuplication(e, metrics)); + const isDup = sameSeriesEvents?.some((e) => checkEventDuplication(e, metrics)); if (isDup) { props.el.classList.add('show-dup-event-error'); eventTitleInput.invalid = true; @@ -426,6 +424,8 @@ export default async function init(component, props) { props.el.classList.remove('show-dup-event-error'); eventTitleInput.invalid = false; } + + eventTitleInput.dispatchEvent(new Event('change')); }); const { @@ -451,6 +451,15 @@ export default async function init(component, props) { changeInputValue(endTimeInput, 'value', localEndTime || ''); changeInputValue(component.querySelector('#time-zone-select-input'), 'value', `${timezone}` || ''); + BlockMediator.set('eventDupMetrics', { + ...BlockMediator.get('eventDupMetrics'), + ...{ + title, + startDate: localStartDate, + eventId: eventData.eventId, + }, + }); + datePicker.dataset.startDate = localStartDate || ''; datePicker.dataset.endDate = localEndDate || ''; updateInput(component, { 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 55ac0ddc..380ecd3e 100644 --- a/ecc/blocks/form-handler/controllers/venue-info-component-controller.js +++ b/ecc/blocks/form-handler/controllers/venue-info-component-controller.js @@ -179,7 +179,7 @@ export default async function init(component, props) { changeInputValue(placeIdInput, 'value', placeId); changeInputValue(mapUrlInput, 'value', mapUrl); changeInputValue(gmtoffsetInput, 'value', venue.gmtOffset); - BlockMediator.set('eventDupMetrics', { ...BlockMediator.get('eventDupMetrics'), placeId }); + BlockMediator.set('eventDupMetrics', { ...BlockMediator.get('eventDupMetrics'), city }); if (venueName) { component.classList.add('prefilled'); diff --git a/ecc/blocks/form-handler/form-handler.css b/ecc/blocks/form-handler/form-handler.css index 479646f5..efb38db6 100644 --- a/ecc/blocks/form-handler/form-handler.css +++ b/ecc/blocks/form-handler/form-handler.css @@ -82,7 +82,10 @@ .form-handler.show-dup-event-error #info-field-event-title { --mod-textfield-icon-size-invalid: 16px; --mod-textfield-border-color-invalid-default: unset; - +} + +.form-handler.show-dup-event-error #info-field-event-title sp-help-text { + display: flex; } .form-handler .main-frame { @@ -103,7 +106,6 @@ justify-content: center; } - .form-handler .side-menu, .form-handler .main-frame, .form-handler .form-handler-ctas-panel, diff --git a/ecc/blocks/form-handler/form-handler.js b/ecc/blocks/form-handler/form-handler.js index 86a4e8db..9f268dd0 100644 --- a/ecc/blocks/form-handler/form-handler.js +++ b/ecc/blocks/form-handler/form-handler.js @@ -638,6 +638,7 @@ function initNavigation(props) { navItems.forEach((nav, i) => { nav.addEventListener('click', async () => { + if (nav.closest('li').classList.contains('active')) return; if (!nav.disabled && !sideMenu.classList.contains('disabled')) { sideMenu.classList.add('disabled'); From edeff810917677226c3b0b871a342974b7ed4fd6 Mon Sep 17 00:00:00 2001 From: Qiyun Dai Date: Wed, 4 Sep 2024 11:39:20 -0500 Subject: [PATCH 08/10] help text tweak --- .../controllers/event-format-component-controller.js | 12 ++++++++++-- .../controllers/event-info-component-controller.js | 7 ++++++- ecc/scripts/utils.js | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/ecc/blocks/form-handler/controllers/event-format-component-controller.js b/ecc/blocks/form-handler/controllers/event-format-component-controller.js index 35be7960..d12bd22b 100644 --- a/ecc/blocks/form-handler/controllers/event-format-component-controller.js +++ b/ecc/blocks/form-handler/controllers/event-format-component-controller.js @@ -1,6 +1,6 @@ /* eslint-disable no-unused-vars */ import { getSeries } from '../../../scripts/esp-controller.js'; -import { MILO_CONFIG, LIBS, BlockMediator } from '../../../scripts/scripts.js'; +import { LIBS, BlockMediator } from '../../../scripts/scripts.js'; import { changeInputValue } from '../../../scripts/utils.js'; const { createTag } = await import(`${LIBS}/utils/utils.js`); @@ -87,7 +87,15 @@ async function populateSeriesOptions(props, component) { seriesSelect.addEventListener('change', () => { const seriesId = seriesSelect.value; - BlockMediator.set('eventDupMetrics', { ...BlockMediator.get('eventDupMetrics'), seriesId }); + const seriesName = seriesSelect.querySelector(`[value="${seriesId}"]`).textContent; + + BlockMediator.set('eventDupMetrics', { + ...BlockMediator.get('eventDupMetrics'), + ...{ + seriesId, + seriesName, + }, + }); }); } diff --git a/ecc/blocks/form-handler/controllers/event-info-component-controller.js b/ecc/blocks/form-handler/controllers/event-info-component-controller.js index 740caf45..0ea0bd9e 100644 --- a/ecc/blocks/form-handler/controllers/event-info-component-controller.js +++ b/ecc/blocks/form-handler/controllers/event-info-component-controller.js @@ -2,7 +2,7 @@ /* eslint-disable no-use-before-define */ import { getEvents } from '../../../scripts/esp-controller.js'; import { BlockMediator, LIBS } from '../../../scripts/scripts.js'; -import { changeInputValue, miloReplaceKey } from '../../../scripts/utils.js'; +import { changeInputValue } from '../../../scripts/utils.js'; const { createTag, getConfig } = await import(`${LIBS}/utils/utils.js`); @@ -415,6 +415,11 @@ export default async function init(component, props) { BlockMediator.subscribe('eventDupMetrics', (store) => { const metrics = store.newValue; + const helpText = component.querySelector('sp-textfield#info-field-event-title sp-help-text'); + + helpText.textContent = helpText.textContent + .replace('[[seriesName]]', metrics.seriesName) + .replace('[[eventName]]', metrics.title); const isDup = sameSeriesEvents?.some((e) => checkEventDuplication(e, metrics)); if (isDup) { diff --git a/ecc/scripts/utils.js b/ecc/scripts/utils.js index e5a05276..997a1e41 100644 --- a/ecc/scripts/utils.js +++ b/ecc/scripts/utils.js @@ -154,7 +154,7 @@ export async function decorateTextfield(cell, extraOptions, negativeHelperText = )); if (negativeHelperText) { - createTag('sp-help-text', { variant: 'negative', slot: 'negative-help-text', icon: true }, negativeHelperText, { parent: input }); + createTag('sp-help-text', { variant: 'negative', slot: 'negative-help-text' }, negativeHelperText, { parent: input }); } if (maxCharNum) input.setAttribute('maxlength', maxCharNum); From ca72724aa015cf00a8da3559830c0acd149a5df7 Mon Sep 17 00:00:00 2001 From: Qiyun Dai Date: Thu, 5 Sep 2024 08:55:46 -0500 Subject: [PATCH 09/10] Update utils.js --- ecc/scripts/utils.js | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/ecc/scripts/utils.js b/ecc/scripts/utils.js index 997a1e41..01744da4 100644 --- a/ecc/scripts/utils.js +++ b/ecc/scripts/utils.js @@ -248,6 +248,41 @@ export async function miloReplaceKey(key) { } } +export function toClassName(name) { + return name && typeof name === 'string' + ? name.toLowerCase().replace(/[^0-9a-z]/gi, '-') + : ''; +} + +export function readBlockConfig(block) { + return [...block.querySelectorAll(':scope>div')].reduce((config, row) => { + if (row.children) { + const cols = [...row.children]; + if (cols[1]) { + const valueEl = cols[1]; + const name = toClassName(cols[0].textContent); + if (valueEl.querySelector('a')) { + const aArr = [...valueEl.querySelectorAll('a')]; + if (aArr.length === 1) { + config[name] = aArr[0].href; + } else { + config[name] = aArr.map((a) => a.href); + } + } else if (valueEl.querySelector('p')) { + const pArr = [...valueEl.querySelectorAll('p')]; + if (pArr.length === 1) { + config[name] = pArr[0].innerHTML; + } else { + config[name] = pArr.map((p) => p.innerHTML); + } + } else config[name] = row.children[1].innerHTML; + } + } + + return config; + }, {}); +} + export const fetchThrottledMemoizedText = (() => { const cache = new Map(); const pending = new Map(); From 0afe6b0190be2db64253005d2beca77348ab9cce Mon Sep 17 00:00:00 2001 From: Qiyun Dai Date: Thu, 5 Sep 2024 09:44:19 -0500 Subject: [PATCH 10/10] reduce new event toast --- ecc/blocks/form-handler/form-handler.js | 32 ++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/ecc/blocks/form-handler/form-handler.js b/ecc/blocks/form-handler/form-handler.js index e557e8ce..f2cb1756 100644 --- a/ecc/blocks/form-handler/form-handler.js +++ b/ecc/blocks/form-handler/form-handler.js @@ -375,6 +375,21 @@ function showSaveSuccessMessage(props, detail = { message: 'Edits saved successf }); } +function updateDashboardLink(props) { + // FIXME: presuming first link is dashboard link is not good. + if (!getFilteredCachedResponse().eventId) return; + const dashboardLink = props.el.querySelector('.side-menu > ul > li > a'); + + if (!dashboardLink) return; + + const url = new URL(dashboardLink.href); + + if (url.searchParams.has('eventId')) return; + + url.searchParams.set('newEventId', getFilteredCachedResponse().eventId); + dashboardLink.href = url.toString(); +} + async function saveEvent(props, options = { toPublish: false }) { try { await gatherValues(props); @@ -395,6 +410,7 @@ async function saveEvent(props, options = { toPublish: false }) { if (props.currentStep === 0 && !getFilteredCachedResponse().eventId) { resp = await createEvent(quickFilter(props.payload)); props.eventDataResp = { ...props.eventDataResp, ...resp }; + updateDashboardLink(props); onEventSave(); } else if (props.currentStep <= props.maxStep && !options.toPublish) { resp = await updateEvent( @@ -655,21 +671,6 @@ function initNavigation(props) { }); } -function updateDashboardLink(props) { - // FIXME: presuming first link is dashboard link is not good. - if (!getFilteredCachedResponse().eventId) return; - const dashboardLink = props.el.querySelector('.side-menu > ul > li > a'); - - if (!dashboardLink) return; - - const url = new URL(dashboardLink.href); - - if (url.searchParams.has('eventId')) return; - - url.searchParams.set('newEventId', getFilteredCachedResponse().eventId); - dashboardLink.href = url.toString(); -} - function initDeepLink(props) { const { hash } = window.location; @@ -751,7 +752,6 @@ async function buildECCForm(el) { case 'eventDataResp': { setResponseCache(value); updateCtas(target); - updateDashboardLink(target); if (value.error) { props.el.classList.add('show-error'); } else {