Skip to content

Commit

Permalink
feat(hub-common): add attendees settings pane (#1475)
Browse files Browse the repository at this point in the history
Co-authored-by: Randy Weber <rweber@esri.com>
  • Loading branch information
juliannaeapicella and rweber-esri committed May 9, 2024
1 parent 568de71 commit 0c49423
Show file tree
Hide file tree
Showing 29 changed files with 1,038 additions and 39 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/common/src/core/schemas/internal/getEditorSchemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,12 +353,16 @@ export async function getEditorSchemas(
import("../../../events/_internal/EventSchemaCreate"),
"hub:event:edit": () =>
import("../../../events/_internal/EventSchemaEdit"),
"hub:event:attendees": () =>
import("../../../events/_internal/EventSchemaAttendeesSettings"),
}[type as EventEditorType]();
const eventUiSchemaModule = await {
"hub:event:create": () =>
import("../../../events/_internal/EventUiSchemaCreate"),
"hub:event:edit": () =>
import("../../../events/_internal/EventUiSchemaEdit"),
"hub:event:attendees": () =>
import("../../../events/_internal/EventUiSchemaAttendeesSettings"),
}[type as EventEditorType]();
schema = eventSchemaModule.buildSchema();
uiSchema = await eventUiSchemaModule.buildUiSchema(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { IConfigurationSchema } from "../../core/schemas/types";

export const buildSchema = (): IConfigurationSchema => {
return {
properties: {
allowRegistration: {
type: "boolean",
enum: [true, false],
default: true,
},
notifyAttendees: {
type: "boolean",
enum: [true, false],
default: true,
},
},
} as IConfigurationSchema;
};
6 changes: 5 additions & 1 deletion packages/common/src/events/_internal/EventSchemaCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import {
} from "./validations";

export type EventEditorType = (typeof EventEditorTypes)[number];
export const EventEditorTypes = ["hub:event:create", "hub:event:edit"] as const;
export const EventEditorTypes = [
"hub:event:create",
"hub:event:edit",
"hub:event:attendees",
] as const;

/**
* @private
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { IArcGISContext } from "../../ArcGISContext";
import { EntityEditorOptions } from "../../core/schemas/internal/EditorOptions";
import { IUiSchema } from "../../core/schemas/types";

export const buildUiSchema = async (
i18nScope: string,
options: EntityEditorOptions,
context: IArcGISContext
): Promise<IUiSchema> => {
return {
type: "Layout",
elements: [
{
type: "Section",
elements: [
{
labelKey: `${i18nScope}.fields.allowRegistration.label`,
scope: "/properties/allowRegistration",
type: "Control",
options: {
control: "hub-field-input-tile-select",
type: "radio",
helperText: {
labelKey: `${i18nScope}.fields.allowRegistration.helperText`,
},
labels: [
`{{${i18nScope}.fields.allowRegistration.enabled.label:translate}}`,
`{{${i18nScope}.fields.allowRegistration.disabled.label:translate}}`,
],
descriptions: [
`{{${i18nScope}.fields.allowRegistration.enabled.description:translate}}`,
`{{${i18nScope}.fields.allowRegistration.disabled.description:translate}}`,
],
icons: ["user-calendar", "circle-disallowed"],
layout: "horizontal",
},
},
{
labelKey: `${i18nScope}.fields.notifyAttendees.label`,
scope: "/properties/notifyAttendees",
type: "Control",
options: {
control: "hub-field-input-tile-select",
type: "radio",
helperText: {
labelKey: `${i18nScope}.fields.notifyAttendees.helperText`,
},
labels: [
`{{${i18nScope}.fields.notifyAttendees.enabled.label:translate}}`,
`{{${i18nScope}.fields.notifyAttendees.disabled.label:translate}}`,
],
descriptions: [
`{{${i18nScope}.fields.notifyAttendees.enabled.description:translate}}`,
`{{${i18nScope}.fields.notifyAttendees.disabled.description:translate}}`,
],
icons: ["envelope", "circle-disallowed"],
layout: "horizontal",
},
},
],
},
],
};
};
15 changes: 15 additions & 0 deletions packages/common/src/events/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
createEvent as createEventApi,
updateEvent as updateEventApi,
} from "./api/events";
import { deleteRegistration } from "./api";

/**
* @private
Expand Down Expand Up @@ -116,3 +117,17 @@ export async function updateHubEvent(

return mapper.storeToEntity(model, {}) as IHubEvent;
}

/**
* @private
* Remove an Event Attendee
* @param id event attendee id
* @param requestOptions
* @returns Promise<void>
*/
export async function deleteHubEventAttendee(
id: number,
requestOptions: IHubRequestOptions
): Promise<void> {
await deleteRegistration({ registrationId: id, ...requestOptions });
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { expandApi } from "../../utils";
import { shouldUseOgcApi } from "./shouldUseOgcApi";
import { getOgcApiDefinition } from "./getOgcApiDefinition";
import { shouldUseDiscussionsApi } from "./shouldUseDiscussionsApi";
import { shouldUseEventsApi } from "./shouldUseEventsApi";
import { getDiscussionsApiDefinition } from "./getDiscussionsApiDefinition";
import { shouldUseEventsApi } from "./shouldUseEventsApi";

/**
* @private
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ export function shouldUseEventsApi(
const {
requestOptions: { isPortal },
} = options;
return targetEntity === "event" && !isPortal;
return ["event", "eventAttendee"].includes(targetEntity) && !isPortal;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { IHubSearchResult } from "../../types/IHubSearchResult";
import { IRegistration } from "../../../events/api/orval/api/orval-events";
import { AccessLevel } from "../../../core";
import { getUser, IUser } from "@esri/arcgis-rest-portal";
import { IHubSearchOptions } from "../../types/IHubSearchOptions";
import { getUserHomeUrl } from "../../../urls/getUserHomeUrl";
import { getUserThumbnailUrl } from "../../utils";

/**
* Transforms a given event attendee into a IHubSearchResult
* @param attendee
* @returns
*/
export async function eventAttendeeToSearchResult(
attendee: IRegistration,
options: IHubSearchOptions
): Promise<IHubSearchResult> {
const [user, creator] = await Promise.all([
getUser({
username: attendee.userId,
...options.requestOptions,
}),
getUser({
username: attendee.createdById,
...options.requestOptions,
}),
]);
return {
id: attendee.id.toString(),
access: user.access as AccessLevel,
name: user.fullName,
createdDate: new Date(attendee.createdAt),
createdDateSource: "attendee.createdAt",
updatedDate: new Date(attendee.updatedAt),
updatedDateSource: "attendee.updatedAt",
type: "Event Attendee",
family: "eventAttendee",
owner: creator.username,
rawResult: attendee,
links: {
self: getUserHomeUrl(user.username, options.requestOptions),
siteRelative: `/people/${user.username}`,
thumbnail: user.thumbnail
? getUserThumbnailUrl(
options.requestOptions.portal,
user,
options.requestOptions.authentication?.token
)
: null,
},
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { unique } from "../../../util";
import { IFilter } from "../../types/IHubCatalog";
import { getPredicateValuesByKey } from "./getPredicateValuesByKey";

export const getOptionalPredicateStringsByKey = (
filters: IFilter[],
predicateKey: string
): string => {
const predicateValues = getPredicateValuesByKey(filters, predicateKey);
const str = predicateValues.filter(unique).join(",");
if (str) {
return str;
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { IFilter } from "../../types/IHubCatalog";

export const getPredicateValuesByKey = (
filters: IFilter[],
predicateKey: string
): any[] => {
const toPredicateValuesByKey = (a1: any[], filter: IFilter): any[] =>
filter.predicates.reduce<any[]>(
(a2, predicate) =>
Object.entries(predicate).reduce(
(a3, [key, val]) => (key === predicateKey ? [...a3, val] : a3),
a2
),
a1
);
return filters.reduce(toPredicateValuesByKey, []);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
EventAttendanceType,
GetRegistrationsParams,
RegistrationRole,
RegistrationStatus,
} from "../../../events/api/types";
import { IQuery } from "../../types";
import { getOptionalPredicateStringsByKey } from "./getOptionalPredicateStringsByKey";
import { getPredicateValuesByKey } from "./getPredicateValuesByKey";

export function processAttendeeFilters(
query: IQuery
): Partial<GetRegistrationsParams> {
const processedFilters: Partial<GetRegistrationsParams> = {
eventId: query.properties.eventId,
};
const useElseJoin = (value: string, defaults: string[]): string =>
value?.length ? value : defaults.map((val) => val.toLowerCase()).join(",");

const term = getPredicateValuesByKey(query.filters, "term");
if (term.length) {
// TODO: remove ts-ignore once GetEventsParams supports filtering by username, firstName, lastName https://devtopia.esri.com/dc/hub/issues/10153
// @ts-ignore
processedFilters.name = term[0];
}

processedFilters.type = useElseJoin(
getOptionalPredicateStringsByKey(query.filters, "attendanceType"),
[EventAttendanceType.VIRTUAL, EventAttendanceType.IN_PERSON]
);

processedFilters.role = useElseJoin(
getOptionalPredicateStringsByKey(query.filters, "role"),
[
RegistrationRole.OWNER,
RegistrationRole.ORGANIZER,
RegistrationRole.ATTENDEE,
]
);

processedFilters.status = useElseJoin(
getOptionalPredicateStringsByKey(query.filters, "status"),
[
RegistrationStatus.PENDING,
RegistrationStatus.ACCEPTED,
RegistrationStatus.DECLINED,
RegistrationStatus.BLOCKED,
]
);

const updatedDateRange = getPredicateValuesByKey(
query.filters,
"updatedDateRange"
);
if (updatedDateRange.length) {
processedFilters.updatedAtBefore = new Date(
updatedDateRange[0].to
).toISOString();
processedFilters.updatedAtAfter = new Date(
updatedDateRange[0].from
).toISOString();
}

return processedFilters;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {
GetRegistrationsParams,
RegistrationSort,
SortOrder,
} from "../../../events/api/types";
import { IHubSearchOptions } from "../../types/IHubSearchOptions";

export function processAttendeeOptions(
options: IHubSearchOptions
): Partial<GetRegistrationsParams> {
const processedOptions: Partial<GetRegistrationsParams> = {};
if (options.num > 0) {
processedOptions.num = options.num.toString();
}
if (options.start > 1) {
processedOptions.start = options.start.toString();
}
if (options.sortField === "modified") {
processedOptions.sortBy = RegistrationSort.updatedAt;
} else if (options.sortField === "created") {
processedOptions.sortBy = RegistrationSort.createdAt;
} else if (options.sortField === "username") {
processedOptions.sortBy = RegistrationSort.username;
} else if (options.sortField === "firstName") {
processedOptions.sortBy = RegistrationSort.firstName;
} else if (options.sortField === "lastName") {
processedOptions.sortBy = RegistrationSort.lastName;
}
processedOptions.sortOrder =
options.sortOrder === "desc" ? SortOrder.desc : SortOrder.asc;
return processedOptions;
}
Original file line number Diff line number Diff line change
@@ -1,36 +1,10 @@
import { IFilter, IPredicate } from "../../types/IHubCatalog";
import { IFilter } from "../../types/IHubCatalog";
import {
EventStatus,
GetEventsParams,
} from "../../../events/api/orval/api/orval-events";
import { unique } from "../../../util";

const getPredicateValuesByKey = (
filters: IFilter[],
predicateKey: string
): any[] => {
const toPredicateValuesByKey = (a1: any[], filter: IFilter): any[] =>
filter.predicates.reduce<any[]>(
(a2, predicate) =>
Object.entries(predicate).reduce(
(a3, [key, val]) => (key === predicateKey ? [...a3, val] : a3),
a2
),
a1
);
return filters.reduce(toPredicateValuesByKey, []);
};

const getOptionalPredicateStringsByKey = (
filters: IFilter[],
predicateKey: string
): string => {
const predicateValues = getPredicateValuesByKey(filters, predicateKey);
const str = predicateValues.filter(unique).join(",");
if (str) {
return str;
}
};
import { getOptionalPredicateStringsByKey } from "./getOptionalPredicateStringsByKey";
import { getPredicateValuesByKey } from "./getPredicateValuesByKey";

/**
* Builds a Partial<GetEventsParams> given an Array of IFilter objects
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/search/_internal/hubSearchChannels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
ISearchChannelsParams,
channelToSearchResult,
} from "../../discussions";
import { IGroup, getGroup } from "@esri/arcgis-rest-portal";
import { getGroup } from "@esri/arcgis-rest-portal";

/**
* @private
Expand Down

0 comments on commit 0c49423

Please sign in to comment.