Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(hub-common): create feedback entity #1414

Merged
merged 32 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e296ebc
feat(hub-common): create feedback entity
juliannaeapicella Feb 14, 2024
56424a3
fix(hub-common): update imports
juliannaeapicella Feb 15, 2024
4c17117
feat(): add survey utils
juliannaeapicella Feb 16, 2024
b79e9bb
Merge branch 'master' into 8849/survey-workspace
juliannaeapicella Feb 16, 2024
c7d9a05
feat(hub-common): add typing
juliannaeapicella Feb 16, 2024
f7e8e13
Merge branch '8849/survey-workspace' of https://github.com/Esri/hub.j…
juliannaeapicella Feb 16, 2024
ad40a38
Merge branch 'master' into 8849/survey-workspace
juliannaeapicella Feb 20, 2024
6f54ffc
fix(hub-common): pr feedback
juliannaeapicella Feb 21, 2024
5adcc1d
Merge branch 'master' into 8849/survey-workspace
juliannaeapicella Feb 21, 2024
14308d6
Merge branch 'master' into 8849/survey-workspace
juliannaeapicella Feb 21, 2024
262f6d0
feat(): additional change in support of adding HubFeedback entity
rweber-esri Feb 23, 2024
03013e7
feat(): a little cleanup
rweber-esri Feb 23, 2024
7c7b15d
Merge branch 'master' into 8849/survey-workspace
juliannaeapicella Feb 23, 2024
be0096a
feat(): remove call to getUniqueSlug as feedback doesn't support slugs
rweber-esri Feb 23, 2024
7c269a3
feat(): fetch form.json from updateFeedback to fix JS error when edit…
rweber-esri Feb 23, 2024
917bb46
feat(): tweak isMapQuestion to check for defaultMap when maps array i…
rweber-esri Feb 23, 2024
88bdc68
fix(): remove call to setEntityStatusKeyword and tweak logic in setDi…
rweber-esri Feb 23, 2024
a4d9021
fix(): rename feedback to survey
juliannaeapicella Feb 23, 2024
3080d4f
fix(): merge errors
juliannaeapicella Feb 23, 2024
2913fe3
feat(hub-common): change radio to tile select
juliannaeapicella Feb 26, 2024
f815011
fix(hub-common): input tile select updates
juliannaeapicella Feb 26, 2024
7619eca
feat(hub-common): some test updates
juliannaeapicella Feb 27, 2024
8133253
feat(hub-common): update survey settings ui schema
juliannaeapicella Feb 28, 2024
9754c03
feat(): test updates first pass
juliannaeapicella Mar 1, 2024
b813428
Merge branch 'master' into 8849/survey-workspace
juliannaeapicella Mar 1, 2024
bf43526
feat(hub-common): small test updates
juliannaeapicella Mar 4, 2024
d71f6a3
fix(hub-common): more test updates
juliannaeapicella Mar 4, 2024
39c32fa
feat(hub-common): separate util tests
juliannaeapicella Mar 4, 2024
edd93eb
Merge branch 'master' into 8849/survey-workspace
juliannaeapicella Mar 4, 2024
3dcbe64
fix(hub-common): test coverage
juliannaeapicella Mar 4, 2024
ff66e3f
Merge branch '8849/survey-workspace' of https://github.com/Esri/hub.j…
juliannaeapicella Mar 4, 2024
cd8b369
Merge branch 'master' into 8849/survey-workspace
juliannaeapicella Mar 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/common/src/core/fetchHubEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { HubEntityType } from "./types/HubEntityType";
import { IArcGISContext } from "../ArcGISContext";
import { fetchHubGroup } from "../groups/HubGroups";
import { fetchInitiativeTemplate } from "../initiative-templates/fetch";
import { fetchFeedback } from "../feedback/fetch";

/**
* Fetch a Hub entity by identifier (id or slug)
Expand Down Expand Up @@ -49,6 +50,9 @@ export async function fetchHubEntity(
case "group":
result = await fetchHubGroup(identifier, context.userRequestOptions);
break;
case "feedback":
result = await fetchFeedback(identifier, context.hubRequestOptions);
break;
case "initiativeTemplate":
result = await fetchInitiativeTemplate(
identifier,
Expand Down
20 changes: 20 additions & 0 deletions packages/common/src/core/schemas/internal/getEditorSchemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
import { IArcGISContext } from "../../../ArcGISContext";
import { InitiativeTemplateEditorType } from "../../../initiative-templates/_internal/InitiativeTemplateSchema";
import { getCardEditorSchemas } from "./getCardEditorSchemas";
import { FeedbackEditorType } from "../../../feedback/_internal/FeedbackSchema";

/**
* get the editor schema and uiSchema defined for an editor (either an entity or a card).
Expand Down Expand Up @@ -221,6 +222,25 @@ export async function getEditorSchemas(
);

break;
// ----------------------------------------------------
case "feedback":
const { FeedbackSchema } = await import(
"../../../feedback/_internal/FeedbackSchema"
);
schema = cloneObject(FeedbackSchema);

const feedbackModule = await {
"hub:feedback:edit": () =>
import("../../../feedback/_internal/FeedbackUiSchemaEdit"),
"hub:feedback:settings": () =>
import("../../../feedback/_internal/FeedbackUiSchemaSettings"),
}[type as FeedbackEditorType]();
uiSchema = await feedbackModule.buildUiSchema(
i18nScope,
options as EntityEditorOptions,
context
);
break;

case "initiativeTemplate":
const { InitiativeTemplateSchema } = await import(
Expand Down
2 changes: 2 additions & 0 deletions packages/common/src/core/schemas/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ContentEditorTypes } from "../../content/_internal/ContentSchema";
import { TemplateEditorTypes } from "../../templates/_internal/TemplateSchema";
import { GroupEditorTypes } from "../../groups/_internal/GroupSchema";
import { InitiativeTemplateEditorTypes } from "../../initiative-templates/_internal/InitiativeTemplateSchema";
import { FeedbackEditorTypes } from "../../feedback/_internal/FeedbackSchema";

export interface IEditorConfig {
schema: IConfigurationSchema;
Expand All @@ -31,6 +32,7 @@ export const validEntityEditorTypes = [
...TemplateEditorTypes,
...GroupEditorTypes,
...InitiativeTemplateEditorTypes,
...FeedbackEditorTypes,
] as const;

/** Defines the possible editor type values for a stat card. These
Expand Down
4 changes: 3 additions & 1 deletion packages/common/src/core/types/HubEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { IHubSite } from "./IHubSite";
import { IHubDiscussion } from "./IHubDiscussion";
import { IHubGroup } from "./IHubGroup";
import { IHubTemplate } from "./IHubTemplate";
import { IHubFeedback } from "./IHubFeedback";

export type HubEntity =
| IHubSite
Expand All @@ -13,4 +14,5 @@ export type HubEntity =
| IHubInitiative
| IHubPage
| IHubGroup
| IHubTemplate;
| IHubTemplate
| IHubFeedback;
1 change: 1 addition & 0 deletions packages/common/src/core/types/HubEntityType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export const HUB_ENTITY_TYPES = [
"org",
"group",
"template",
"feedback",
] as const;
export type HubEntityType = (typeof HUB_ENTITY_TYPES)[number];
10 changes: 10 additions & 0 deletions packages/common/src/core/types/IHubFeedback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { IWithPermissions } from "../traits";
import { IHubItemEntity, IHubItemEntityEditor } from "./IHubItemEntity";

/**
* Defines properties of Hub Feedback object
* @internal
*/
export interface IHubFeedback extends IHubItemEntity, IWithPermissions {}

export type IHubFeedbackEditor = IHubItemEntityEditor<IHubFeedback> & {};
1 change: 1 addition & 0 deletions packages/common/src/core/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export * from "./IHubDiscussion";
export * from "./IHubEditableContent";
export * from "./IHubEntityBase";
export * from "./IHubEvent";
export * from "./IHubFeedback";
export * from "./IHubGroup";
export * from "./IHubImage";
export * from "./IHubInitiative";
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/discussions/HubDiscussion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export class HubDiscussion
}

/*
* Get the editor config for the HubProject entity.
* Get the editor config for the HubDiscussion entity.
* @param i18nScope translation scope to be interpolated into the uiSchema
* @param type editor type - corresonds to the returned uiSchema
* @param options optional hash of dynamic uiSchema element options
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const DEFAULT_ENTITY_SETTINGS_BY_ENTITY_TYPE: Record<
org: null,
group: null,
template: null,
feedback: null,
};

export function getDefaultEntitySettings(
Expand Down
149 changes: 149 additions & 0 deletions packages/common/src/feedback/HubFeedback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { HubItemEntity } from "../core/HubItemEntity";
import { IWithEditorBehavior } from "../core/behaviors/IWithEditorBehavior";
import { enrichEntity } from "../core/enrichEntity";
import { getEditorConfig } from "../core/schemas/getEditorConfig";
import { IEditorConfig } from "../core/schemas/types";
import { HubEntity } from "../core/types/HubEntity";
import {
HubEntityEditor,
IEntityEditorContext,
} from "../core/types/HubEntityEditor";
import { IHubFeedback, IHubFeedbackEditor } from "../core/types/IHubFeedback";
import { cloneObject } from "../util";
import { FeedbackEditorType } from "./_internal/FeedbackSchema";
import { deleteFeedback, updateFeedback } from "./edit";

/**
* Hub Feedback Class
*/
export class HubFeedback
juliannaeapicella marked this conversation as resolved.
Show resolved Hide resolved
extends HubItemEntity<IHubFeedback>
implements IWithEditorBehavior
{
/**
* Apply a new state to the instance
* @param changes A partial IHubFeedback
*/
update(changes: Partial<IHubFeedback>): void {
if (this.isDestroyed) {
throw new Error("HubFeedback is already destroyed.");
}
// merge partial onto existing entity
this.entity = { ...this.entity, ...changes };
}

/**
* Save the class instance
*/
async save(): Promise<void> {
if (this.isDestroyed) {
throw new Error("HubFeedback is already destroyed.");
}

if (this.entity.id) {
// update it
this.entity = await updateFeedback(
this.entity,
this.context.userRequestOptions
);
}

// call the after save hook on superclass
await super.afterSave();

return;
}

/**
* Delete the HubFeedback object from the store
* set a flag to indicate that it is destroyed
* @returns a promise
*/
async delete(): Promise<void> {
if (this.isDestroyed) {
throw new Error("HubFeedback is already destroyed.");
}
this.isDestroyed = true;
// Delegate to module fn
await deleteFeedback(this.entity.id, this.context.userRequestOptions);
}

/*
* Get the editor config for the HubFeedback entity.
* @param i18nScope translation scope to be interpolated into the uiSchema
* @param type editor type - corresonds to the returned uiSchema
*/
getEditorConfig(
i18nScope: string,
type: FeedbackEditorType
): Promise<IEditorConfig> {
// delegate to the schema subsystem
return getEditorConfig(i18nScope, type, this.entity, this.context);
}

/**
* Return the feedback object as an editor object
* @param editorContext
* @param include
* @returns
*/
async toEditor(
editorContext: IEntityEditorContext,
include?: string[]
): Promise<HubEntityEditor> {
// 1. optionally enrich entity and cast to editor
const editor = include.length
? ((await enrichEntity(
cloneObject(this.entity),
include,
this.context.hubRequestOptions
)) as IHubFeedbackEditor)
: (cloneObject(this.entity) as IHubFeedbackEditor);

// 2. Apply transforms to relevant entity values so they
// can be consumed by the editor
return editor;
}

/**
* Load the feedback object from the editor object
* @param editor
* @param editorContext
* @returns IHubFeedback
*/
async fromEditor(
editor: HubEntityEditor,
editorContext?: IEntityEditorContext
): Promise<HubEntity> {
// Setting the thumbnailCache will ensure that
// the thumbnail is updated on next save
if (editor._thumbnail) {
if (editor._thumbnail.blob) {
this.thumbnailCache = {
file: editor._thumbnail.blob,
filename: editor._thumbnail.fileName,
clear: false,
};
} else {
this.thumbnailCache = {
clear: true,
};
}
}

delete editor._thumbnail;

// convert back to an entity. Apply any reverse transforms used in
// of the toEditor method
const entity = cloneObject(editor) as IHubFeedback;

// copy the location extent up one level
entity.extent = editor.location?.extent;

// Save, which will also create new content if new
this.entity = entity;
await this.save();

return this.entity;
}
}
75 changes: 75 additions & 0 deletions packages/common/src/feedback/_internal/FeedbackBusinessRules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { IPermissionPolicy } from "../../permissions/types/IPermissionPolicy";

/**
* Feedback Permission Policies
* These define the requirements any user must meet to perform related actions
* @private
*/
export const FeedbackPermissions = [
"hub:feedback",
"hub:feedback:create",
"hub:feedback:delete",
"hub:feedback:edit",
"hub:feedback:view",
"hub:feedback:workspace",
"hub:feedback:workspace:dashboard",
"hub:feedback:workspace:details",
"hub:feedback:workspace:settings",
"hub:feedback:manage",
] as const;

/**
* Feedback permission policies
* @private
*/
export const FeedbackPermissionPolicies: IPermissionPolicy[] = [
{
permission: "hub:feedback",
services: ["portal"],
juliannaeapicella marked this conversation as resolved.
Show resolved Hide resolved
// gated for now
availability: ["alpha"],
environments: ["devext", "qaext"],
},
{
permission: "hub:feedback:view",
dependencies: ["hub:feedback"],
},
{
permission: "hub:feedback:create",
authenticated: true,
dependencies: ["hub:feedback"],
entityEdit: true,
},
{
permission: "hub:feedback:edit",
authenticated: true,
dependencies: ["hub:feedback"],
entityEdit: true,
},
{
permission: "hub:feedback:delete",
authenticated: true,
dependencies: ["hub:feedback"],
entityOwner: true,
},
{
permission: "hub:feedback:workspace",
dependencies: ["hub:feature:workspace"],
},
{
permission: "hub:feedback:workspace:dashboard",
dependencies: ["hub:feedback:workspace", "hub:feedback:view"],
},
{
permission: "hub:feedback:workspace:details",
dependencies: ["hub:feedback:workspace", "hub:feedback:edit"],
},
{
permission: "hub:feedback:workspace:settings",
dependencies: ["hub:feedback:workspace", "hub:feedback:edit"],
},
{
permission: "hub:feedback:manage",
dependencies: ["hub:feedback:edit"],
},
];
28 changes: 28 additions & 0 deletions packages/common/src/feedback/_internal/FeedbackSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { HubItemEntitySchema } from "../../core/schemas/shared/HubItemEntitySchema";
import { IConfigurationSchema } from "../../core/schemas/types";

export type FeedbackEditorType = (typeof FeedbackEditorTypes)[number];
export const FeedbackEditorTypes = [
"hub:feedback:edit",
"hub:feedback:settings",
] as const;

/**
* defines the JSON schema for a Feedback entity's editable fields
*/
export const FeedbackSchema: IConfigurationSchema = {
...HubItemEntitySchema,
properties: {
displayMap: {
juliannaeapicella marked this conversation as resolved.
Show resolved Hide resolved
type: "boolean",
enum: [true, false],
default: false,
},
juliannaeapicella marked this conversation as resolved.
Show resolved Hide resolved
hasMapQuestion: {
type: "boolean",
enum: [true, false],
default: false,
},
...HubItemEntitySchema.properties,
},
} as IConfigurationSchema;
Loading
Loading