Skip to content

Commit

Permalink
[MWPW-157579] Event Title Duplication Prevention (#200)
Browse files Browse the repository at this point in the history
  • Loading branch information
qiyundai authored Sep 5, 2024
1 parent 1e2d471 commit 5284d5c
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 30 deletions.
11 changes: 10 additions & 1 deletion ecc/blocks/event-info-component/event-info-component.css
Original file line number Diff line number Diff line change
Expand Up @@ -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%;
Expand All @@ -33,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;
Expand Down
11 changes: 9 additions & 2 deletions ecc/blocks/event-info-component/event-info-component.js
Original file line number Diff line number Diff line change
@@ -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`);

Expand Down Expand Up @@ -94,7 +101,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' }, await miloReplaceKey('duplicate-event-title-error'));
break;
case 2:
await decorateTextarea(r, { id: 'info-field-event-description', grows: true, quiet: true });
Expand Down
Original file line number Diff line number Diff line change
@@ -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 { LIBS, BlockMediator } from '../../../scripts/scripts.js';
import { changeInputValue } from '../../../scripts/utils.js';

const { createTag } = await import(`${LIBS}/utils/utils.js`);
Expand Down Expand Up @@ -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;

Expand All @@ -84,6 +84,19 @@ async function populateSeriesOptions(component) {
});

seriesSelect.pending = false;

seriesSelect.addEventListener('change', () => {
const seriesId = seriesSelect.value;
const seriesName = seriesSelect.querySelector(`[value="${seriesId}"]`).textContent;

BlockMediator.set('eventDupMetrics', {
...BlockMediator.get('eventDupMetrics'),
...{
seriesId,
seriesName,
},
});
});
}

export default async function init(component, props) {
Expand All @@ -104,7 +117,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,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable no-unused-vars */
/* eslint-disable no-use-before-define */
import { LIBS } from '../../../scripts/scripts.js';
import { getEvents } from '../../../scripts/esp-controller.js';
import { BlockMediator, LIBS } from '../../../scripts/scripts.js';
import { changeInputValue } from '../../../scripts/utils.js';

const { createTag, getConfig } = await import(`${LIBS}/utils/utils.js`);
Expand Down Expand Up @@ -345,14 +346,36 @@ export async function onUpdate(component, props) {
// do nothing
}

export default function init(component, props) {
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;
}

export default async function init(component, props) {
const allEventsResp = await getEvents();
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');
const endTimeInput = component.querySelector('#time-picker-end-time');
const datePicker = component.querySelector('#event-info-date-picker');

initCalendar(component);

eventTitleInput.addEventListener('input', () => {
BlockMediator.set('eventDupMetrics', { ...BlockMediator.get('eventDupMetrics'), title: eventTitleInput.value });
});

endTimeInput.addEventListener('change', () => {
if (datePicker.dataset.startDate !== datePicker.dataset.endDate) return;
const allOptions = startTimeInput.querySelectorAll('sp-menu-item');
Expand Down Expand Up @@ -386,6 +409,28 @@ export default function init(component, props) {
option.disabled = false;
});
}

BlockMediator.set('eventDupMetrics', { ...BlockMediator.get('eventDupMetrics'), startDate: datePicker.dataset.startDate });
});

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) {
props.el.classList.add('show-dup-event-error');
eventTitleInput.invalid = true;
} else {
props.el.classList.remove('show-dup-event-error');
eventTitleInput.invalid = false;
}

eventTitleInput.dispatchEvent(new Event('change'));
});

const {
Expand All @@ -411,6 +456,15 @@ export default 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, {
Expand Down
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -97,6 +97,7 @@ function initAutocomplete(el) {
changeInputValue(mapUrl, 'value', place.url);

togglePrefillableFieldsHiddenState(el, false);
BlockMediator.set('eventDupMetrics', { ...BlockMediator.get('eventDupMetrics'), city: addressInfo.city });
}

if (place.geometry) {
Expand All @@ -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;

Expand Down Expand Up @@ -178,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'), city });

if (venueName) {
component.classList.add('prefilled');
Expand Down
11 changes: 10 additions & 1 deletion ecc/blocks/form-handler/form-handler.css
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@

.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.show-dup-event-error #info-field-event-title sp-help-text {
display: flex;
}

.form-handler .main-frame {
Expand All @@ -96,7 +106,6 @@
justify-content: center;
}


.form-handler .side-menu,
.form-handler .main-frame,
.form-handler .form-handler-ctas-panel,
Expand Down
35 changes: 18 additions & 17 deletions ecc/blocks/form-handler/form-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand All @@ -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(
Expand Down Expand Up @@ -638,6 +654,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');

Expand All @@ -654,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;

Expand Down Expand Up @@ -750,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 {
Expand Down
24 changes: 23 additions & 1 deletion ecc/scripts/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -153,6 +153,10 @@ export async function decorateTextfield(cell, extraOptions) {
extraOptions,
));

if (negativeHelperText) {
createTag('sp-help-text', { variant: 'negative', slot: 'negative-help-text' }, negativeHelperText, { parent: input });
}

if (maxCharNum) input.setAttribute('maxlength', maxCharNum);

const wrapper = createTag('div', { class: 'info-field-wrapper' });
Expand Down Expand Up @@ -226,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 function toClassName(name) {
return name && typeof name === 'string'
? name.toLowerCase().replace(/[^0-9a-z]/gi, '-')
Expand Down

0 comments on commit 5284d5c

Please sign in to comment.