Skip to content

Commit

Permalink
feat(hub-common): add events location picker (#1546)
Browse files Browse the repository at this point in the history
Co-authored-by: Randy Weber <[email protected]>
  • Loading branch information
juliannaeapicella and rweber-esri authored Jun 14, 2024
1 parent 8c25692 commit 0205b32
Show file tree
Hide file tree
Showing 13 changed files with 448 additions and 56 deletions.
4 changes: 2 additions & 2 deletions packages/common/src/core/types/IHubEvent.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
HubEventAttendanceType,
HubEventOnlineCapacityType,
HubEventCapacityType,
} from "../../events/types";
import { IWithPermissions, IWithSlug } from "../traits";
import { IHubItemEntity, IHubItemEntityEditor } from "./IHubItemEntity";
Expand Down Expand Up @@ -118,7 +118,7 @@ export interface IHubEvent extends IHubItemEntity, IWithPermissions, IWithSlug {
/**
* The capacity type for an online event, either `unlimited` or `fixed`
*/
onlineCapacityType?: HubEventOnlineCapacityType;
onlineCapacityType?: HubEventCapacityType;

/**
* The details for an online event
Expand Down
22 changes: 16 additions & 6 deletions packages/common/src/events/_internal/EventSchemaEdit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import {
ENTITY_TAGS_SCHEMA,
} from "../../core/schemas/shared/subschemas";
import { IConfigurationSchema } from "../../core/schemas/types";
import { HubEventAttendanceType, HubEventOnlineCapacityType } from "../types";
import { HubEventAttendanceType, HubEventCapacityType } from "../types";
import {
URL_VALIDATIONS_WHEN_ONLINE_OR_HYBRID,
TIME_VALIDATIONS_WHEN_NOT_ALL_DAY,
FIXED_ONLINE_ATTENDANCE_VALIDATIONS,
FIXED_IN_PERSON_ATTENDANCE_VALIDATIONS,
} from "./validations";
import { getDefaultEventDatesAndTimes } from "./getDefaultEventDatesAndTimes";

Expand Down Expand Up @@ -59,6 +60,17 @@ export const buildSchema = (): IConfigurationSchema => {
timeZone: {
type: "string",
},
inPersonCapacity: {
type: "number",
},
inPersonCapacityType: {
type: "string",
enum: [HubEventCapacityType.Unlimited, HubEventCapacityType.Fixed],
default: HubEventCapacityType.Unlimited,
},
location: {
type: "object",
},
onlineUrl: {
type: "string",
},
Expand All @@ -70,11 +82,8 @@ export const buildSchema = (): IConfigurationSchema => {
},
onlineCapacityType: {
type: "string",
enum: [
HubEventOnlineCapacityType.Unlimited,
HubEventOnlineCapacityType.Fixed,
],
default: HubEventOnlineCapacityType.Unlimited,
enum: [HubEventCapacityType.Unlimited, HubEventCapacityType.Fixed],
default: HubEventCapacityType.Unlimited,
},
summary: ENTITY_SUMMARY_SCHEMA,
tags: ENTITY_TAGS_SCHEMA,
Expand All @@ -84,6 +93,7 @@ export const buildSchema = (): IConfigurationSchema => {
URL_VALIDATIONS_WHEN_ONLINE_OR_HYBRID,
TIME_VALIDATIONS_WHEN_NOT_ALL_DAY,
FIXED_ONLINE_ATTENDANCE_VALIDATIONS,
FIXED_IN_PERSON_ATTENDANCE_VALIDATIONS,
],
} as IConfigurationSchema;
};
91 changes: 87 additions & 4 deletions packages/common/src/events/_internal/EventUiSchemaEdit.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { IUiSchema, UiSchemaRuleEffects } from "../../core/schemas/types";
import { IArcGISContext } from "../../ArcGISContext";
import { EntityEditorOptions } from "../../core/schemas/internal/EditorOptions";
import { getDatePickerDate } from "../../utils/date/getDatePickerDate";
import { IHubEvent } from "../../core/types/IHubEvent";
import { getCategoryItems } from "../../core/schemas/internal/getCategoryItems";
import { getTagItems } from "../../core/schemas/internal/getTagItems";
import { HubEventAttendanceType, HubEventOnlineCapacityType } from "../types";
import { HubEventAttendanceType, HubEventCapacityType } from "../types";
import { getLocationExtent } from "../../core/schemas/internal/getLocationExtent";
import { getLocationOptions } from "../../core/schemas/internal/getLocationOptions";

/**
* @private
Expand All @@ -15,7 +16,7 @@ import { HubEventAttendanceType, HubEventOnlineCapacityType } from "../types";
*/
export const buildUiSchema = async (
i18nScope: string,
options: EntityEditorOptions,
options: Partial<IHubEvent>,
context: IArcGISContext
): Promise<IUiSchema> => {
const minStartEndDate = getDatePickerDate(
Expand Down Expand Up @@ -205,6 +206,88 @@ export const buildUiSchema = async (
enum: { i18nScope: `${i18nScope}.fields.attendanceType` },
},
},
{
scope: "/properties/location",
type: "Control",
labelKey: `${i18nScope}.fields.location.label`,
options: {
control: "hub-field-input-location-picker",
extent: await getLocationExtent(
options.location,
context.hubRequestOptions
),
options: await getLocationOptions(
options.id,
options.type,
options.location,
context.portal.name,
context.hubRequestOptions
),
mapTools: ["polygon", "rectangle"],
},
},
{
labelKey: `${i18nScope}.fields.inPersonCapacityType.label`,
scope: "/properties/inPersonCapacityType",
type: "Control",
rule: {
condition: {
scope: "/properties/attendanceType",
schema: {
enum: [
HubEventAttendanceType.InPerson,
HubEventAttendanceType.Both,
],
},
},
effect: UiSchemaRuleEffects.SHOW,
},
options: {
control: "hub-field-input-radio-group",
enum: { i18nScope: `${i18nScope}.fields.inPersonCapacityType` },
},
},
{
labelKey: `${i18nScope}.fields.inPersonCapacity.label`,
scope: "/properties/inPersonCapacity",
type: "Control",
rule: {
condition: {
schema: {
properties: {
attendanceType: {
enum: [
HubEventAttendanceType.InPerson,
HubEventAttendanceType.Both,
],
},
inPersonCapacityType: {
const: HubEventCapacityType.Fixed,
},
},
},
},
effect: UiSchemaRuleEffects.SHOW,
},
options: {
control: "hub-field-input-input",
type: "number",
messages: [
{
type: "ERROR",
keyword: "required",
icon: true,
labelKey: `${i18nScope}.fields.inPersonCapacity.requiredError`,
},
{
type: "ERROR",
keyword: "minimum",
icon: true,
labelKey: `${i18nScope}.fields.inPersonCapacity.minimumError`,
},
],
},
},
{
labelKey: `${i18nScope}.fields.onlineUrl.label`,
scope: "/properties/onlineUrl",
Expand Down Expand Up @@ -299,7 +382,7 @@ export const buildUiSchema = async (
],
},
onlineCapacityType: {
const: HubEventOnlineCapacityType.Fixed,
const: HubEventCapacityType.Fixed,
},
},
},
Expand Down
50 changes: 42 additions & 8 deletions packages/common/src/events/_internal/PropertyMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import { cloneObject } from "../../util";
import {
EventAccess,
EventAttendanceType,
EventLocationType,
EventStatus,
IEvent,
IEventLocation,
IOnlineMeeting,
} from "../api/orval/api/orval-events";
import { HubEventAttendanceType, HubEventOnlineCapacityType } from "../types";
import { HubEventAttendanceType, HubEventCapacityType } from "../types";
import { computeLinks } from "./computeLinks";
import { getEventSlug } from "./getEventSlug";
import { getEventThumbnail } from "./getEventThumbnail";
Expand Down Expand Up @@ -45,9 +47,6 @@ export class EventPropertyMapper extends PropertyMapper<
store: Partial<IEvent>,
entity: Partial<IHubEvent>
): Partial<IHubEvent> {
// TODO: support locations
// TODO: thumbnail & thumbnail url

const obj = mapStoreToEntity(store, entity, this.mappings);

obj.type = "Event";
Expand All @@ -67,8 +66,11 @@ export class EventPropertyMapper extends PropertyMapper<
}
obj.onlineCapacity = store.onlineMeetings?.[0]?.capacity ?? null;
obj.onlineCapacityType = store.onlineMeetings?.[0]?.capacity
? HubEventOnlineCapacityType.Fixed
: HubEventOnlineCapacityType.Unlimited;
? HubEventCapacityType.Fixed
: HubEventCapacityType.Unlimited;
obj.inPersonCapacityType = store.inPersonCapacity
? HubEventCapacityType.Fixed
: HubEventCapacityType.Unlimited;
obj.onlineDetails = store.onlineMeetings?.[0]?.details ?? null;
obj.onlineUrl = store.onlineMeetings?.[0]?.url ?? null;
obj.canChangeAccess = [
Expand Down Expand Up @@ -109,7 +111,19 @@ export class EventPropertyMapper extends PropertyMapper<
tooltip,
});
}
obj.view = { heroActions };
obj.view = {
heroActions,
showMap: !!store.location,
};

obj.location = store.location
? {
type: store.location.type,
spatialReference: store.location.spatialReference,
extent: store.location.extent,
geometries: store.location.geometries,
}
: { type: "none" };

return obj;
}
Expand Down Expand Up @@ -167,20 +181,40 @@ export class EventPropertyMapper extends PropertyMapper<
{
details: clonedEntity.onlineDetails,
capacity:
clonedEntity.onlineCapacityType === HubEventOnlineCapacityType.Fixed
clonedEntity.onlineCapacityType === HubEventCapacityType.Fixed
? clonedEntity.onlineCapacity
: null,
url: clonedEntity.onlineUrl,
} as IOnlineMeeting,
];
}
if (
[HubEventAttendanceType.InPerson, HubEventAttendanceType.Both].includes(
clonedEntity.attendanceType
)
) {
obj.inPersonCapacity =
clonedEntity.inPersonCapacityType === HubEventCapacityType.Fixed
? clonedEntity.inPersonCapacity
: null;
}

// override startTime & endTime for all-day events
if (clonedEntity.isAllDay) {
clonedEntity.startTime = "00:00:00";
clonedEntity.endTime = "23:59:59";
}

obj.location =
clonedEntity.location && clonedEntity.location.type !== "none"
? ({
type: clonedEntity.location.type as EventLocationType,
spatialReference: clonedEntity.location.spatialReference,
extent: clonedEntity.location.extent,
geometries: clonedEntity.location.geometries,
} as IEventLocation)
: null;

return obj;
}
}
23 changes: 21 additions & 2 deletions packages/common/src/events/_internal/validations.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HubEventAttendanceType, HubEventOnlineCapacityType } from "../types";
import { HubEventAttendanceType, HubEventCapacityType } from "../types";

export const TIME_VALIDATIONS_WHEN_NOT_ALL_DAY = {
if: {
Expand Down Expand Up @@ -38,7 +38,7 @@ export const URL_VALIDATIONS_WHEN_ONLINE_OR_HYBRID = {
export const FIXED_ONLINE_ATTENDANCE_VALIDATIONS = {
if: {
properties: {
onlineCapacityType: { const: HubEventOnlineCapacityType.Fixed },
onlineCapacityType: { const: HubEventCapacityType.Fixed },
attendanceType: {
enum: [HubEventAttendanceType.Online, HubEventAttendanceType.Both],
},
Expand All @@ -53,3 +53,22 @@ export const FIXED_ONLINE_ATTENDANCE_VALIDATIONS = {
},
},
};

export const FIXED_IN_PERSON_ATTENDANCE_VALIDATIONS = {
if: {
properties: {
inPersonCapacityType: { const: HubEventCapacityType.Fixed },
attendanceType: {
enum: [HubEventAttendanceType.InPerson, HubEventAttendanceType.Both],
},
},
},
then: {
required: ["inPersonCapacity"],
properties: {
inPersonCapacity: {
minimum: 1,
},
},
},
};
9 changes: 8 additions & 1 deletion packages/common/src/events/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
EventStatus,
IEvent,
} from "./api/orval/api/orval-events";
import { HubEventAttendanceType } from "./types";
import { HubEventAttendanceType, HubEventCapacityType } from "./types";

/**
* Builds a partial IHubEvent (entity) with default values
Expand All @@ -19,6 +19,7 @@ export function buildDefaultEventEntity(): Partial<IHubEvent> {
attendanceType: HubEventAttendanceType.InPerson,
categories: [],
inPersonCapacity: null,
inPersonCapacityType: HubEventCapacityType.Unlimited,
isAllDay: false,
isCanceled: false,
isDiscussable: true,
Expand All @@ -27,6 +28,7 @@ export function buildDefaultEventEntity(): Partial<IHubEvent> {
name: "",
notifyAttendees: true,
onlineCapacity: null,
onlineCapacityType: HubEventCapacityType.Unlimited,
onlineDetails: null,
onlineUrl: null,
references: [],
Expand All @@ -37,6 +39,10 @@ export function buildDefaultEventEntity(): Partial<IHubEvent> {
...dates,
view: {
heroActions: [],
showMap: false,
},
location: {
type: "none",
},
};
}
Expand All @@ -61,5 +67,6 @@ export function buildDefaultEventRecord(): Partial<IEvent> {
status: EventStatus.PLANNED,
tags: [],
title: "",
location: null,
} as Partial<IEvent>;
}
Loading

0 comments on commit 0205b32

Please sign in to comment.