diff --git a/src/app/_samples/mediaco/components/banner/banner.component.scss b/src/app/_samples/mediaco/components/banner/banner.component.scss
index 39ee2ebc..8ec6e20a 100644
--- a/src/app/_samples/mediaco/components/banner/banner.component.scss
+++ b/src/app/_samples/mediaco/components/banner/banner.component.scss
@@ -34,11 +34,11 @@
}
.background-image-style {
- height: calc(20rem);
position: sticky;
top: var(--mat-toolbar-standard-height, 64px);
background: var(--mat-toolbar-container-background-color, var(--mat-sys-surface));
z-index: 10;
+ padding-bottom: 2rem;
}
.background-style {
diff --git a/src/app/_samples/mediaco/components/list-view/DefaultViewMeta.ts b/src/app/_samples/mediaco/components/list-view/DefaultViewMeta.ts
new file mode 100644
index 00000000..8970a4f5
--- /dev/null
+++ b/src/app/_samples/mediaco/components/list-view/DefaultViewMeta.ts
@@ -0,0 +1,220 @@
+const LOCALIZATON_ANNOTATION = '@L ';
+const PROPERTY_ANNOTATION = '@P .';
+const USER_ANNOTATION = '@USER .';
+const ASSOCIATED_ANNOTATION = '@ASSOCIATED .';
+const ASSOCIATION_ANNOTATION = '@CA ';
+
+const getDefaultConfig = (fieldMeta, classID, show) => {
+ const {
+ name,
+ label,
+ fieldID,
+ fieldType,
+ dataType,
+ type,
+ classID: fieldMetaClassID,
+ displayAs,
+ displayAsLink,
+ category,
+ associationClassID,
+ associationID
+ } = fieldMeta;
+ return {
+ value: (associationClassID ? ASSOCIATION_ANNOTATION : PROPERTY_ANNOTATION).concat(fieldID),
+ label: LOCALIZATON_ANNOTATION.concat(name || label),
+ fieldType,
+ propertyType: dataType || type,
+ classID: classID || fieldMetaClassID,
+ displayAs,
+ displayAsLink,
+ category,
+ show,
+ ...(associationClassID ? { associationLabel: LOCALIZATON_ANNOTATION.concat(category) } : {}),
+ associationID
+ };
+};
+
+export function getDefaultViewMeta(fieldMeta, classID, showField) {
+ const { type, name, displayAs, fieldID, isUserReference, associationID, datasource, label, fieldType } = fieldMeta;
+ const mapperKey = type && displayAs ? type.concat(':').concat(displayAs) : type;
+ const defaultConfig = getDefaultConfig(fieldMeta, classID, showField);
+ let viewMeta;
+ switch (mapperKey) {
+ case 'True-False:pxCheckbox':
+ viewMeta = {
+ type: 'Checkbox',
+ config: {
+ ...defaultConfig,
+ trueLabel: '@L Yes',
+ falseLabel: '@L No',
+ caption: LOCALIZATON_ANNOTATION.concat(name || label),
+ label: undefined
+ }
+ };
+ break;
+ case 'Decimal:pxCurrency':
+ viewMeta = {
+ type: 'Currency',
+ config: defaultConfig
+ };
+ break;
+ case 'Date Time:pxDateTime':
+ case 'Date & time:pxDateTime':
+ viewMeta = {
+ type: 'DateTime',
+ config: defaultConfig
+ };
+ break;
+ case 'Date:pxDateTime':
+ case 'Date only:pxDateTime':
+ viewMeta = {
+ type: 'Date',
+ config: defaultConfig
+ };
+ break;
+ case 'Decimal:pxNumber':
+ viewMeta = {
+ type: 'Decimal',
+ config: defaultConfig
+ };
+ break;
+ case 'Text:pxEmail':
+ viewMeta = {
+ type: 'Email',
+ config: defaultConfig
+ };
+ break;
+ case 'Integer:pxInteger':
+ viewMeta = {
+ type: 'Integer',
+ config: defaultConfig
+ };
+ break;
+ case 'Decimal:pxPercentage':
+ viewMeta = {
+ type: 'Percentage',
+ config: defaultConfig
+ };
+ break;
+ case 'Text:pxPhone':
+ viewMeta = {
+ type: 'Phone',
+ config: {
+ ...defaultConfig,
+ datasource: {
+ source: '@DATASOURCE D_pyCountryCallingCodeList.pxResults',
+ fields: {
+ value: '@P .pyCallingCode'
+ }
+ }
+ }
+ };
+ break;
+ case 'TimeOfDay:pxDateTime':
+ viewMeta = {
+ type: 'Time',
+ config: defaultConfig
+ };
+ break;
+ case 'Text:pxURL':
+ case 'Text:pxUrl':
+ viewMeta = {
+ type: 'URL',
+ config: defaultConfig
+ };
+ break;
+ case 'Text:pxTextArea':
+ viewMeta = {
+ type: 'TextArea',
+ config: defaultConfig
+ };
+ break;
+ case 'Text:pxRichTextEditor':
+ viewMeta = {
+ type: 'RichText',
+ config: defaultConfig
+ };
+ break;
+ case 'Text:pxAutoComplete':
+ if (isUserReference || fieldType === 'User reference') {
+ viewMeta = {
+ type: 'UserReference',
+ config: {
+ ...defaultConfig,
+ value: USER_ANNOTATION.concat(fieldID),
+ placeholder: 'Select...',
+ displayAs: 'Search box',
+ associationID,
+ associationLabel: undefined
+ }
+ };
+ } else {
+ const { tableType = '' } = datasource || {};
+ viewMeta = {
+ type: 'AutoComplete',
+ config: {
+ ...defaultConfig,
+ placeholder: 'Select...',
+ listType: 'associated',
+ datasource: ASSOCIATED_ANNOTATION.concat(fieldID),
+ deferDatasource: tableType === 'DataPage'
+ }
+ };
+ }
+ break;
+ case 'Text:pxDropdown':
+ if (isUserReference || fieldType === 'User reference') {
+ viewMeta = {
+ type: 'UserReference',
+ config: {
+ ...defaultConfig,
+ value: USER_ANNOTATION.concat(fieldID),
+ placeholder: 'Select...',
+ displayAs: 'Drop-down list',
+ associationID,
+ associationLabel: undefined
+ }
+ };
+ } else {
+ const { tableType = '' } = datasource || {};
+ viewMeta = {
+ type: 'Dropdown',
+ config: {
+ ...defaultConfig,
+ placeholder: 'Select...',
+ listType: 'associated',
+ datasource: ASSOCIATED_ANNOTATION.concat(fieldID),
+ deferDatasource: tableType === 'DataPage'
+ }
+ };
+ }
+ break;
+ case 'Text:pxRadioButtons':
+ {
+ const { tableType = '' } = datasource || {};
+ viewMeta = {
+ type: 'RadioButtons',
+ config: {
+ ...defaultConfig,
+ placeholder: 'Select...',
+ listType: 'associated',
+ datasource: ASSOCIATED_ANNOTATION.concat(fieldID),
+ deferDatasource: tableType === 'DataPage'
+ }
+ };
+ }
+ break;
+ case 'Text:pxTextInput':
+ viewMeta = {
+ type: 'TextInput',
+ config: defaultConfig
+ };
+ break;
+ default:
+ viewMeta = {
+ type,
+ config: defaultConfig
+ };
+ }
+ return viewMeta;
+}
diff --git a/src/app/_samples/mediaco/components/list-view/list-view.component.ts b/src/app/_samples/mediaco/components/list-view/list-view.component.ts
index 489bdc40..346c95ef 100644
--- a/src/app/_samples/mediaco/components/list-view/list-view.component.ts
+++ b/src/app/_samples/mediaco/components/list-view/list-view.component.ts
@@ -7,6 +7,8 @@ import { MatDialog } from '@angular/material/dialog';
import { MatIconButton } from '@angular/material/button';
import { MatIcon } from '@angular/material/icon';
+import { init } from './listViewHelpers';
+
interface ListViewProps {
inheritedProps: any;
title: string | undefined;
@@ -46,6 +48,14 @@ export class ListViewComponent implements OnInit {
referenceDataPage: string;
caseTypeToActivityMap: any;
title: string;
+ payload: any;
+ listContext: any = {};
+ ref: any = {};
+ showDynamicFields: boolean | undefined;
+ cosmosTableRef: any;
+ selectionMode: string | undefined;
+ fieldDefs: any;
+ columns: any[];
constructor(
public utils: Utils,
@@ -65,11 +75,35 @@ export class ListViewComponent implements OnInit {
this.configProps$ = this.pConn$.getConfigProps() as ListViewProps;
this.template = this.configProps$.presets[0]?.template;
this.title = this.configProps$.title || '';
- this.getListData();
+ this.showDynamicFields = this.configProps$?.showDynamicFields;
+ this.selectionMode = this.configProps$.selectionMode;
+
+ if (this.configProps$) {
+ if (!this.payload) {
+ this.payload = { referenceList: this.configProps$.referenceList };
+ }
+ init({
+ pConn$: this.pConn$,
+ bInForm$: this.bInForm$,
+ ...this.payload,
+ listContext: this.listContext,
+ ref: this.ref,
+ showDynamicFields: this.showDynamicFields,
+ cosmosTableRef: this.cosmosTableRef,
+ selectionMode: this.selectionMode
+ }).then(response => {
+ this.listContext = response;
+ this.getListData();
+ });
+ }
}
getListData() {
+ this.fieldDefs = this.listContext.meta.fieldDefs;
this.referenceDataPage = this.configProps$.referenceList;
+ const componentConfig = this.pConn$.getComponentConfig();
+ const columnFields = componentConfig.presets[0].children[0].children;
+ this.columns = this.getHeaderCells(columnFields, this.fieldDefs);
PCore.getDataPageUtils()
.getDataAsync(this.referenceDataPage, this.pConn$.getContextName())
.then(({ data }) => {
@@ -83,9 +117,9 @@ export class ListViewComponent implements OnInit {
const caseType = this.caseTypeToActivityMap[item.ActivityType];
return {
icon: this.utils.getImageSrc(this.getIcon(caseType), this.utils.getSDKStaticContentUrl()),
- title: item.ActivityType,
- title_subtext: this.timeSince(new Date(item.pxUpdateDateTime || item.pxCreateDateTime)),
- description: item.Description
+ title: this.columns[0] ? item[this.columns[0]?.id] : undefined,
+ title_subtext: this.columns[2] ? this.timeSince(new Date(item[this.columns[2]?.id] || item.pxCreateDateTime)) : undefined,
+ description: this.columns[1] ? item[this.columns[1]?.id] : undefined
};
});
return;
@@ -94,10 +128,10 @@ export class ListViewComponent implements OnInit {
this.sourceList = data.map((item, index) => {
return {
number: index + 1,
- title: item.Name,
- description: item.Genere,
- description_subtext: item.Views + ' views',
- rating: item.Rating
+ title: this.columns[0] ? item[this.columns[0]?.id] : undefined,
+ description: this.columns[1] ? item[this.columns[1]?.id] : undefined,
+ description_subtext: this.columns[2] ? item[this.columns[2]?.id] + ' views' : undefined,
+ rating: item[this.columns[3]?.id]
};
});
return;
@@ -174,4 +208,33 @@ export class ListViewComponent implements OnInit {
}
});
}
+
+ private getHeaderCells(colFields, fields) {
+ const AssignDashObjects = ['Assign-Worklist', 'Assign-WorkBasket'];
+ return colFields.map((field: any, index) => {
+ let theField = field.config.value.substring(field.config.value.indexOf(' ') + 1);
+ if (theField.indexOf('.') === 0) {
+ theField = theField.substring(1);
+ }
+ const colIndex = fields.findIndex(ele => ele.name === theField);
+ const displayAsLink = field.config.displayAsLink;
+ const headerRow: any = {};
+ headerRow.id = fields[index].id;
+ headerRow.type = field.type;
+ headerRow.displayAsLink = displayAsLink;
+ headerRow.numeric = field.type === 'Decimal' || field.type === 'Integer' || field.type === 'Percentage' || field.type === 'Currency' || false;
+ headerRow.disablePadding = false;
+ headerRow.label = fields[index].label;
+ if (colIndex > -1) {
+ headerRow.classID = fields[colIndex].classID;
+ }
+ if (displayAsLink) {
+ headerRow.isAssignmentLink = AssignDashObjects.includes(headerRow.classID);
+ if (field.config.value?.startsWith('@CA')) {
+ headerRow.isAssociation = true;
+ }
+ }
+ return headerRow;
+ });
+ }
}
diff --git a/src/app/_samples/mediaco/components/list-view/listViewHelpers.ts b/src/app/_samples/mediaco/components/list-view/listViewHelpers.ts
new file mode 100644
index 00000000..aab719ea
--- /dev/null
+++ b/src/app/_samples/mediaco/components/list-view/listViewHelpers.ts
@@ -0,0 +1,80 @@
+import { getContext, readContextResponse } from './utils';
+
+export function init(props) {
+ const {
+ referenceList,
+ pConn$,
+ personalizationId,
+ parameters,
+ compositeKeys,
+ isSearchable,
+ allowBulkActions,
+ ref,
+ showDynamicFields,
+ isDataObject,
+ cosmosTableRef
+ } = props;
+ let { editing, selectionMode } = props;
+
+ const runtimeParams = PCore.getRuntimeParamsAPI().getRuntimeParams();
+
+ let selectionCountThreshold;
+
+ // promise to fetch metadata
+ const metaDataPromise = PCore.getAnalyticsUtils().getDataViewMetadata(referenceList, showDynamicFields, null);
+
+ const promisesArray: any = [metaDataPromise];
+
+ // promise to fetch report configured columns
+ const reportColumnsPromise = PCore.getAnalyticsUtils()
+ .getFieldsForDataSource(referenceList, false, pConn$.getContextName())
+ .catch(() => {
+ return Promise.resolve({
+ data: { data: [] }
+ });
+ });
+ promisesArray.push(reportColumnsPromise);
+
+ const fetchEditDetails = async metadata => {
+ const {
+ data: { isQueryable }
+ } = metadata;
+ if (!isDataObject) {
+ if (!isQueryable) {
+ editing = false; /* Force editing to false if DP is non queryable */
+ }
+
+ const { MULTI_ON_HOVER, MULTI } = PCore.getConstants().LIST_SELECTION_MODE;
+ if (allowBulkActions && isQueryable) {
+ /** enable bulk actions only if DP is queryable */
+ selectionMode = MULTI_ON_HOVER;
+ }
+ if ([MULTI_ON_HOVER, MULTI].includes(selectionMode)) {
+ selectionCountThreshold = 250; // Results count should not be greater than threshold to display SelectAll checkbox.
+ }
+ }
+ return Promise.resolve();
+ };
+
+ const editPromise = metaDataPromise.then(metadata => fetchEditDetails(metadata));
+ promisesArray.push(editPromise);
+ return getContext({
+ tableSource: referenceList,
+ ListId: personalizationId,
+ runtimeParams: parameters ?? runtimeParams,
+ promisesArray,
+ pConn$,
+ compositeKeys,
+ isSearchable,
+ isCacheable: true
+ }).then(async context => {
+ return readContextResponse(context, {
+ ...props,
+ editing,
+ selectionCountThreshold,
+ ref,
+ selectionMode,
+ cosmosTableRef
+ });
+ });
+}
diff --git a/src/app/_samples/mediaco/components/list-view/utils.ts b/src/app/_samples/mediaco/components/list-view/utils.ts
new file mode 100644
index 00000000..d88523c2
--- /dev/null
+++ b/src/app/_samples/mediaco/components/list-view/utils.ts
@@ -0,0 +1,767 @@
+import { getDefaultViewMeta } from './DefaultViewMeta';
+
+const USER_REFERENCE = 'UserReference';
+const PAGE = '!P!';
+const PAGELIST = '!PL!';
+
+export const formatConstants = {
+ WorkStatus: 'WorkStatus',
+ Integer: 'Integer',
+ WorkLink: 'WorkLink'
+};
+
+class DataApi {
+ mappedPropertyToOriginalProperty: any;
+ originalPropertyToMappedProperty: any;
+ constructor() {
+ this.originalPropertyToMappedProperty = {};
+ this.mappedPropertyToOriginalProperty = {};
+ this.setPropertyMaps = this.setPropertyMaps.bind(this);
+ this.getMappedProperty = this.getMappedProperty.bind(this);
+ this.getOriginalProperty = this.getOriginalProperty.bind(this);
+ }
+
+ setPropertyMaps(originalToMappedPropertyObj = {}, mappedToOriginalPropertyObj = {}) {
+ this.originalPropertyToMappedProperty = {
+ ...this.originalPropertyToMappedProperty,
+ ...originalToMappedPropertyObj
+ };
+ this.mappedPropertyToOriginalProperty = {
+ ...this.mappedPropertyToOriginalProperty,
+ ...mappedToOriginalPropertyObj
+ };
+ }
+
+ getMappedProperty(propertyName) {
+ return this.originalPropertyToMappedProperty[propertyName] ?? propertyName;
+ }
+
+ getOriginalProperty(propertyName) {
+ return this.mappedPropertyToOriginalProperty[propertyName] ?? propertyName;
+ }
+}
+
+export async function getContext(componentConfig) {
+ const {
+ promisesArray = [] // array of promises which can be invoked paralelly,
+ } = componentConfig;
+ const promisesResponseArray = await Promise.all(promisesArray);
+ const dataApi = new DataApi();
+ return {
+ promisesResponseArray,
+ setPropertyMaps: dataApi.setPropertyMaps,
+ getMappedProperty: dataApi.getMappedProperty,
+ getOriginalProperty: dataApi.getOriginalProperty
+ };
+}
+
+/**
+ * [getFieldNameFromEmbeddedFieldName]
+ * Description - converting embeddedField name starting with !P! or !PL! to normal field
+ * @ignore
+ * @param {string} propertyName EmbeddedField name starting with !P! or !PL!
+ * @returns {string} returns converted string without !P! or !PL! and :
+ *
+ * @example
Example for getFieldNameFromEmbeddedFieldName
+ * getFieldNameFromEmbeddedFieldName('!P!Organisation:Name') return 'Organisation.Name'
+ * getFieldNameFromEmbeddedFieldName('!PL!Employees:Name') return 'Employees.Name'
+ */
+export function getFieldNameFromEmbeddedFieldName(propertyName) {
+ let value = propertyName;
+ if (value.startsWith(PAGE) || value.startsWith(PAGELIST)) {
+ value = value.substring(value.lastIndexOf('!') + 1);
+ value = value.replace(/:/g, '.');
+ }
+ return value;
+}
+
+/**
+ * [updateMetaEmbeddedFieldID]
+ * Description - If the fieldID in meta starts with '!P!' or '!PL!' and contains ':' then replace them with .(dot)
+ * @ignore
+ * @param {Array} metaFields Fields metadata Array. Contains metadata of all the fields.
+ */
+export function updateMetaEmbeddedFieldID(metaFields) {
+ return metaFields.forEach(metaField => {
+ if (metaField.fieldID?.startsWith(PAGE) || metaField.fieldID?.startsWith(PAGELIST)) {
+ metaField.fieldID = getFieldNameFromEmbeddedFieldName(metaField.fieldID);
+ }
+ });
+}
+
+export const isEmbeddedField = field => {
+ if (field?.startsWith('@')) {
+ field = field.substring(field.indexOf(' ') + 1);
+ if (field[0] === '.') field = field.substring(1);
+ }
+ return field?.indexOf('.') > 0;
+};
+
+/**
+ * [isPageListProperty]
+ * Description - checking if propertyName is pageList or not
+ * @ignore
+ * @param {string} propertyName PropertyName
+ * @returns {boolean} true if property is pageList else false
+ *
+ * @example Example for isPageListProperty
+ * isPageListProperty('!PL!Employees.Name') return true
+ * isPageListProperty('!P!Employees.Name') return false
+ * isPageListProperty('Name') return false
+ */
+export function isPageListProperty(propertyName) {
+ return propertyName.startsWith(PAGELIST);
+}
+
+export const isPageListInPath = (propertyName, currentClassID) => {
+ if (!propertyName.includes('.')) {
+ return false;
+ }
+ const [first, ...rest] = propertyName.split('.');
+ const metadata: any = PCore.getMetadataUtils().getPropertyMetadata(first, currentClassID);
+ if (metadata?.type === 'Page List') {
+ return true;
+ }
+ return isPageListInPath(rest.join('.'), metadata?.pageClass);
+};
+
+/**
+ * [getEmbeddedFieldName]
+ * Description - converting normal field name to embedded field starting with !P! or !PL!
+ * @ignore
+ * @param {string} propertyName Field name
+ * @param {string} classID classID of datapage
+ * @returns {string} returns converted string with !P! or !PL! and :
+ *
+ * @example Example for getEmbeddedFieldName
+ * For page property, getEmbeddedFieldName('Organisation.Name') return '!P!Organisation:Name'
+ * For pageList property, getEmbeddedFieldName('Employees.Name') return '!PL!Employees:Name'
+ */
+
+export function getEmbeddedFieldName(propertyName, classID) {
+ let value = propertyName;
+ if (isPageListInPath(value, classID)) {
+ value = `!PL!${value.replace(/\./g, ':')}`;
+ } else {
+ value = `!P!${value.replace(/\./g, ':')}`;
+ }
+ return value;
+}
+
+/**
+ * [preparePropertyMaps]
+ * Description - preparing maps for property names and set it in dataApi context
+ * @ignore
+ * @param {Array} fields fields array
+ * @param {string} classID classID of datapage
+ * @param {string} context dataApi context
+ * @returns {boolean} true if pageListProperty is present
+ */
+export function preparePropertyMaps(fields, classID, context) {
+ const { setPropertyMaps } = context;
+ const maps = fields.reduce(
+ (acc, field) => {
+ let { value } = field.config;
+ if (value.startsWith('@')) {
+ value = value.substring(value.indexOf(' ') + 1);
+ if (value[0] === '.') value = value.substring(1);
+ }
+ let name = value;
+ // Preparing name for embedded property
+ if (isEmbeddedField(name)) {
+ name = getEmbeddedFieldName(name, classID);
+ }
+ if (isPageListProperty(name) && !acc[2]) {
+ acc[2] = true;
+ }
+ acc[0][value] = name;
+ acc[1][name] = value;
+
+ return acc;
+ },
+ [{}, {}, false]
+ );
+ setPropertyMaps(maps[0], maps[1]);
+ return maps[2];
+}
+
+/**
+ * [getConfigEmbeddedFieldsMeta]
+ * Description - Get the metadata for configured embedded fields
+ * @ignore
+ * @param {Set} configFields Set of config fields
+ * @param {string} classID clasID of datapage
+ * @returns {Array} Metadata of configured embedded fields
+ */
+export function getConfigEmbeddedFieldsMeta(configFields, classID) {
+ const configEmbeddedFieldsMeta: any[] = [];
+ configFields.forEach(field => {
+ let value = field;
+ if (isEmbeddedField(value)) {
+ // conversion Page.PageList[].property => Page.PageList.property
+ if (value.includes('[')) {
+ value = value.substring(0, value.indexOf('[')) + value.substring(value.indexOf(']') + 1);
+ }
+ const meta: any = PCore.getMetadataUtils().getEmbeddedPropertyMetadata(value, classID);
+ meta.fieldID = field;
+ configEmbeddedFieldsMeta.push(meta);
+ }
+ });
+ return configEmbeddedFieldsMeta;
+}
+
+/**
+ * [mergeConfigEmbeddedFieldsMeta]
+ * Description - Get the metadata for configured embedded fields
+ * @ignore
+ * @param {Array} configEmbeddedFieldsMeta config fields metadata.
+ * @param {Array} metaFields Fields metadata Array. Contains metadata of all the fields
+ */
+export function mergeConfigEmbeddedFieldsMeta(configEmbeddedFieldsMeta, metaFields) {
+ const mergedMetaFields = [...metaFields];
+ configEmbeddedFieldsMeta.forEach(configFieldMeta => {
+ const fieldMeta = metaFields.find(metaField => metaField.fieldID === configFieldMeta.fieldID);
+ if (!fieldMeta) mergedMetaFields.push(configFieldMeta);
+ });
+ return mergedMetaFields;
+}
+
+const oldToNewFieldTypeMapping = {
+ 'Date Time': 'Date & time',
+ Date: 'Date only'
+};
+
+/**
+ * [updateFieldType]
+ * Description - Updates the field type if its changed in the new implementation. Such mapping is maintained in oldToNewFieldTypeMapping.
+ * @ignore
+ * @param {Array} metaFields Fields metadata Array. Contains metadata of all the fields.
+ */
+function updateFieldType(metaFields) {
+ metaFields.forEach(metaField => {
+ if (metaField.type) metaField.type = oldToNewFieldTypeMapping[metaField.type] || metaField.type;
+ });
+}
+
+function getPresetMetaAttribute(attribute) {
+ const {
+ type,
+ config: { label, value }
+ } = attribute;
+ return {
+ type,
+ name: value.startsWith('@') ? value.substring(4) : value,
+ label: label.startsWith('@') ? label.substring(3) : label
+ };
+}
+
+/**
+ * [generateViewMetaData]
+ * Description - Returns a list of metaobjects of the fields provided.
+ * @ignore
+ * @param {Array} rawFields List of fields to update meta for
+ * @param {string} classID Class ID from the response
+ * @returns {Array} List of fields with updated meta objects.
+ */
+function generateViewMetaData(rawFields, classID, showField) {
+ return rawFields.map(item => getDefaultViewMeta(item, classID, showField));
+}
+
+/**
+ * [getConfigFields]
+ * Description - Returns list of config fields with primary fields meta updated.
+ * @ignore
+ * @param {Array} configFields List of Authored fields
+ * @param {Array} primaryFields List of Primary Fields
+ * @param {Array} metaFields Metadata of all fields
+ * @param {string} classID Class ID from the response
+ * @returns {Array} List of all fields with their meta updated.
+ */
+function getConfigFields(configFields, primaryFields, metaFields, classID) {
+ const presetConfigFields = configFields;
+ const primaryFieldsViewIndex = presetConfigFields.findIndex(field => field.config.value === 'pyPrimaryFields');
+ if (!primaryFields || !primaryFields.length) {
+ if (primaryFieldsViewIndex < 0) return presetConfigFields;
+
+ presetConfigFields.splice(primaryFieldsViewIndex, 1);
+
+ return presetConfigFields;
+ }
+
+ if (primaryFieldsViewIndex > -1) {
+ // list of uncommon fields - non overlap of primary fields grouped view and independent entity columns of primary type
+ const uncommonFieldsList = primaryFields.filter(
+ primaryField => !presetConfigFields.some(presetConfigField => presetConfigField.config.value.split('.')[1] === primaryField)
+ );
+ const uncommonFieldsRawMeta: any[] = [];
+ uncommonFieldsList.forEach(uncommonField => {
+ const uncommonFieldMeta = metaFields.find(metaField => metaField.fieldID === uncommonField);
+ if (uncommonFieldMeta) uncommonFieldsRawMeta.push(uncommonFieldMeta);
+ });
+ const uncommonFieldsConfigMeta = generateViewMetaData(uncommonFieldsRawMeta, classID, true);
+
+ presetConfigFields.splice(primaryFieldsViewIndex, 1, ...uncommonFieldsConfigMeta);
+ }
+ return presetConfigFields;
+}
+
+/**
+ * [getTableConfigFromPresetMeta]
+ * Description - Get the table config from the presets meta.
+ * @ignore
+ * @param {object} presetMeta Presets meta
+ * @param {boolean} isMetaWithPresets true if meta has presets else false
+ * @param {Function} getPConnect Callback to get the PConnect object
+ * @param {string} classID Class ID from the response
+ * @param {Array} primaryFields List of Primary Fields
+ * @param {Array} metaFields List of all metafields
+ * @returns {object} Table config object
+ */
+export function getTableConfigFromPresetMeta(presetMeta, isMetaWithPresets, pConn$, classID, primaryFields, metaFields) {
+ let presetId;
+ let presetName;
+ let cardHeader;
+ let secondaryText;
+ let timelineDate;
+ let timelineTitle;
+ let timelineStatus;
+ let timelineIcon;
+ let { filterExpression } = pConn$.getRawMetadata().config;
+ let fieldsMeta;
+ let configFields;
+ if (isMetaWithPresets) {
+ presetId = presetMeta.id;
+ presetName = presetMeta.label;
+ cardHeader = presetMeta.cardHeader && getPresetMetaAttribute(presetMeta.cardHeader);
+ secondaryText = presetMeta.secondaryText && getPresetMetaAttribute(presetMeta.secondaryText);
+ timelineDate = presetMeta.timelineDate && getPresetMetaAttribute(presetMeta.timelineDate);
+ timelineTitle = presetMeta.timelineTitle && getPresetMetaAttribute(presetMeta.timelineTitle);
+ timelineStatus = presetMeta.timelineStatus && getPresetMetaAttribute(presetMeta.timelineStatus);
+ timelineIcon = presetMeta.timelineIcon && getPresetMetaAttribute(presetMeta.timelineIcon);
+ filterExpression = presetMeta.config.filterExpression;
+ [fieldsMeta] = presetMeta.children;
+ if (
+ presetMeta.timelineTitle &&
+ !fieldsMeta.children.find(fieldMeta => {
+ return fieldMeta?.config?.value === presetMeta.timelineTitle?.config?.value;
+ })
+ ) {
+ const { type, config } = presetMeta.timelineTitle;
+ fieldsMeta.children.push({ type, config: { ...config, show: false } });
+ }
+ if (
+ presetMeta.timelineDate &&
+ !fieldsMeta.children.find(fieldMeta => {
+ return fieldMeta?.config?.value === presetMeta.timelineDate?.config?.value;
+ })
+ ) {
+ const { type, config } = presetMeta.timelineDate;
+ fieldsMeta.children.push({ type, config: { ...config, show: false } });
+ }
+ configFields = getConfigFields(fieldsMeta.children, primaryFields, metaFields, classID);
+ } else {
+ fieldsMeta = presetMeta.props;
+ configFields = getConfigFields(
+ fieldsMeta
+ .getPConnect()
+ .getChildren()
+ ?.map(child => {
+ return child.getPConnect().getRawMetadata();
+ }),
+ primaryFields,
+ metaFields,
+ classID
+ );
+ }
+ return {
+ presetId,
+ presetName,
+ cardHeader,
+ secondaryText,
+ timelineDate,
+ timelineTitle,
+ timelineStatus,
+ timelineIcon,
+ filterExpression,
+ fieldsMeta,
+ configFields
+ };
+}
+
+/**
+ * [getReportColumns]
+ * Description - Returns a set of columns from the report response.
+ * @ignore
+ * @param {object} response -
+ * @returns {Set} Set of columns from the report response
+ */
+function getReportColumns(response) {
+ const {
+ data: { data: reportColumns }
+ } = response;
+ const reportColumnsSet = new Set();
+ reportColumns?.forEach(item => {
+ let val = item.pyFieldName;
+ // Remove '.' from index 0 only, if '.' is present
+ if (val[0] === '.') {
+ val = val.substring(1);
+ }
+ reportColumnsSet.add(val);
+ });
+ return reportColumnsSet;
+}
+
+/**
+ * [getConfigFieldValue]
+ * Descritpion - Returns a valid value for a configuration field. Remove any annotations and also "."
+ * @ignore
+ * @param {object} config
+ * config.value - Raw value
+ * @returns {string} value - Value with out any annotations or "."
+ */
+function getConfigFieldValue(config) {
+ let { value } = config;
+ if (value.startsWith('@')) {
+ value = value.substring(value.indexOf(' ') + 1);
+ if (value[0] === '.') value = value.substring(1);
+ }
+ return value;
+}
+
+/**
+ * [prepareConfigFields]
+ * Description - Prepares a set of configuration fields and pushes each config type to a set using the callback parameter.
+ * @ignore
+ * @param {object} configFields List of Authored fields
+ * @param {Function} pushToComponentsList Callback to push the field type to a set.
+ * @returns {Set} configFieldSet
+ */
+function prepareConfigFields(configFields, pushToComponentsList) {
+ const configFieldSet = new Set();
+ configFields.forEach(item => {
+ pushToComponentsList(item.type);
+ const val = getConfigFieldValue(item.config);
+ configFieldSet.add(val);
+ });
+ return configFieldSet;
+}
+
+/**
+ * [findAuthoredField]
+ * Description - Finds an authored field from yhe list of config fields.
+ * @ignore
+ * @param {Array} configFields List of Authored fields
+ * @param {string} fieldID Filter
+ * @returns {object} config with its field value equal to fieldID, which means an authored field
+ */
+function findAuthoredField(configFields, fieldID) {
+ return configFields.find(configField => {
+ const val = getConfigFieldValue(configField.config);
+ return val === fieldID;
+ });
+}
+
+/**
+ * [findAndUpdateAuthoredFieldConfig]
+ * Description - Find the authored field, and update its config.
+ * @ignore
+ * @param {Array} configFields List of Authored fields
+ * @param {object} item Field item to copy displayAs and category information
+ * @param {string} classId classID from the response
+ */
+function findAndUpdateAuthoredFieldConfig(configFields, item, classId) {
+ const authoredField = findAuthoredField(configFields, item.fieldID);
+ if (authoredField?.config) {
+ if (item.displayAs) {
+ authoredField.config.displayAs = item.displayAs;
+ }
+ authoredField.config.classID = item.classID ?? classId;
+
+ if (authoredField.type === USER_REFERENCE) {
+ authoredField.config.associationID = item.associationID || item.fieldID;
+ }
+ authoredField.config.category = item.category;
+ // FieldType identifies whether the field is configured as pickList
+ authoredField.config.fieldType = item.fieldType;
+ // type defined on the property rule,
+ // used for the picklist field to assign the appropriate type
+ authoredField.config.propertyType = item.dataType || item.type;
+ }
+}
+
+/**
+ * [isAnExtraField]
+ * Description - Returns true if the field is an extra field. Extra field is the one which is not authored but part of the report.
+ * @ignore
+ * @param {Array} configFields List of Authored fields
+ * @param {Set} configFieldSet Set if Authored filed values
+ * @param {Set} reportColumnsSet Set of columns from the report
+ * @param {object} item Config field item
+ * @param {string} classId Class ID from the response
+ * @param {boolean} showDynamicFields Flag indicating whether fields are fetched dynamically at runtime
+ * @returns {boolean} true If the field is an extra field else false.
+ */
+function isAnExtraField(configFields, configFieldSet, reportColumnsSet, item, classId, showDynamicFields) {
+ // Is the field already present in authoring metadata?
+ // Mutates config fields to copy displayAs and category information
+ if (configFieldSet.has(item.fieldID)) {
+ findAndUpdateAuthoredFieldConfig(configFields, item, classId);
+ return false;
+ }
+
+ // If field is not authored and not part of report columns then discard it
+ return showDynamicFields || !!reportColumnsSet.has(item.fieldID);
+}
+
+/**
+ * [prepareExtraFields]
+ * Description - Returns a list of extra fields with their meta updated.
+ * @ignore
+ * @param {Array} metaFields List of fields
+ * @param {Array} configFields List of Authored fields
+ * @param {Set} configFieldSet Set if Authored filed values
+ * @param {Set} reportColumnsSet Set of columns from the report
+ * @param {string} classID Class ID from the response
+ * @param {boolean} showDynamicFields Flag indicating whether fields are fetched dynamically at runtime
+ * @returns {Array} List of extra fields with their meta updated.
+ */
+function prepareExtraFields(metaFields, configFields, configFieldSet, reportColumnsSet, classID, showDynamicFields) {
+ // Filter all the extra fields
+ const extraFileds = metaFields.filter(item => {
+ return isAnExtraField(configFields, configFieldSet, reportColumnsSet, item, classID, showDynamicFields);
+ });
+ return generateViewMetaData(extraFileds, classID, false);
+ // Update the meta object of each of the extra fields.
+}
+
+const AssignDashObjects = ['Assign-Worklist', 'Assign-WorkBasket'];
+
+function populateRenderingOptions(name, config, field) {
+ const shouldDisplayAsSemanticLink = 'displayAsLink' in field.config && field.config.displayAsLink;
+ if (shouldDisplayAsSemanticLink) {
+ config.customObject.isAssignmentLink = AssignDashObjects.includes(field.config.classID);
+ if (field.config.value.startsWith('@CA')) {
+ config.customObject.isAssociation = true;
+ }
+ config.cellRenderer = formatConstants.WorkLink;
+ } else if (name === 'pyStatusWork' || name === 'pyAssignmentStatus') {
+ config.cellRenderer = formatConstants.WorkStatus;
+ } else if (name === 'pxUrgencyWork') {
+ config.cellRenderer = formatConstants.Integer;
+ }
+}
+function isFLProperty(label) {
+ return label?.startsWith('@FL');
+}
+
+function getFieldLabel(fieldConfig) {
+ const { label, classID, caption } = fieldConfig;
+ let fieldLabel = (label ?? caption)?.substring(4);
+ const labelSplit = fieldLabel?.split('.');
+ const propertyName = labelSplit?.pop();
+ const fieldMetaData: any = PCore.getMetadataUtils().getPropertyMetadata(propertyName, classID) ?? {};
+ fieldLabel = fieldMetaData.label ?? fieldMetaData.caption ?? propertyName;
+
+ const definedOnClassID = fieldMetaData.definedOnClassID;
+ const localeValue = PCore.getLocaleUtils().getLocaleValue(
+ fieldLabel,
+ `${definedOnClassID ?? fieldMetaData.classID ?? classID}.${propertyName}`,
+ PCore.getLocaleUtils().FIELD_LABELS_BUNDLE_KEY,
+ null
+ );
+ return localeValue || fieldLabel;
+}
+export function initializeColumns(fields: any[] = [], getMappedProperty: any = null) {
+ return fields.map((field, originalColIndex) => {
+ let name = field.config.value;
+
+ if (name.startsWith('@')) {
+ name = name.substring(name.indexOf(' ') + 1);
+ if (name[0] === '.') name = name.substring(1);
+ }
+ name = getMappedProperty?.(name) ?? name;
+
+ let label = field.config.label || field.config.caption;
+ const { show = true, displayAs } = field.config;
+ if (isFLProperty(label)) {
+ label = getFieldLabel(field.config);
+ } else if (label.startsWith('@')) {
+ label = label.substring(3);
+ }
+
+ const config = {
+ name,
+ label,
+ show,
+ classID: field.config.classID,
+ id: field.id || name || originalColIndex,
+ displayAs,
+ associationID: field.config.associationID,
+ ...(field.config.classID && { category: field.config.category }),
+ customObject: {},
+ fieldType: field.config.fieldType,
+ meta: {
+ ...field
+ }
+ };
+
+ populateRenderingOptions(name, config, field);
+
+ return config;
+ });
+}
+
+export const getItemKey = fields => {
+ let itemKey;
+ if (fields.findIndex(field => field.id === 'pyGUID') > -1) {
+ itemKey = 'pyGUID';
+ } else {
+ itemKey = 'pzInsKey';
+ }
+ return itemKey;
+};
+
+export function preparePatchQueryFields(fields, isDataObject = false, classID = '') {
+ const queryFields: any[] = [];
+ fields.forEach(field => {
+ const patchFields: any[] = [];
+ if (field.cellRenderer === 'WorkLink') {
+ if (field.customObject && field.customObject.isAssignmentLink) {
+ const associationName = field.name.includes(':') ? `${field.name.split(':')[0]}:` : '';
+ patchFields.push(`${associationName}pzInsKey`);
+ patchFields.push(`${associationName}pxRefObjectClass`);
+ } else if (field.customObject && field.customObject.isAssociation) {
+ const associationCategory = field.name.split(':')[0];
+ patchFields.push(`${associationCategory}:pyID`);
+ patchFields.push(`${associationCategory}:pzInsKey`);
+ patchFields.push(`${associationCategory}:pxObjClass`);
+ } else if (isDataObject) {
+ const dataViewName = PCore.getDataTypeUtils().getSavableDataPage(classID);
+ const dataPageKeys = PCore.getDataTypeUtils().getDataPageKeys(dataViewName);
+ dataPageKeys?.forEach(item => (item.isAlternateKeyStorage ? patchFields.push(item.linkedField) : patchFields.push(item.keyName)));
+ } else {
+ patchFields.push('pyID');
+ patchFields.push('pzInsKey');
+ patchFields.push('pxObjClass');
+ }
+ }
+ patchFields.forEach(k => {
+ if (!queryFields.find(q => q === k)) {
+ queryFields.push(k);
+ }
+ });
+ });
+
+ return queryFields;
+}
+
+/**
+ * Update the renderer type for the properties of type Page.
+ */
+export function updatePageFieldsConfig(configFields, parentClassID) {
+ return configFields.forEach(item => {
+ const {
+ type,
+ config: { value }
+ } = item;
+ const propertyName = PCore.getAnnotationUtils().getPropertyName(value);
+ if (isEmbeddedField(value) && !isPageListInPath(propertyName, parentClassID)) {
+ item.config.componentType = type;
+ item.type = 'PagePropertyRenderer';
+ }
+ });
+}
+
+export const readContextResponse = async (context, params) => {
+ const { pConn$, apiContext, children, showDynamicFields, referenceList, isDataObject } = params;
+ // let { listContext } = params;
+ const { promisesResponseArray, apiContext: otherContext } = context;
+
+ const listOfComponents: any[] = [];
+ const {
+ data: { fields: metaFields, classID, isQueryable }
+ } = promisesResponseArray[0];
+ let {
+ data: { primaryFields }
+ } = promisesResponseArray[0];
+ // When list is configured with Include all class fields configuration, provide support for Primary fields column
+ if (showDynamicFields) {
+ const sourceMetadata = PCore.getMetadataUtils().getDataPageMetadata(referenceList);
+ if (sourceMetadata?.primaryFields) {
+ primaryFields = sourceMetadata.primaryFields;
+ }
+ // updating metaData fieldID to normal property if it has fieldID starts with !P! or !PL!
+ updateMetaEmbeddedFieldID(metaFields);
+ }
+ updateFieldType(metaFields);
+
+ if (isDataObject) {
+ const compositeKeys: any[] = [];
+ const dataViewName = PCore.getDataTypeUtils().getSavableDataPage(classID);
+ const dataPageKeys = PCore.getDataTypeUtils().getDataPageKeys(dataViewName);
+ dataPageKeys?.forEach(item => (item.isAlternateKeyStorage ? compositeKeys.push(item.linkedField) : compositeKeys.push(item.keyName)));
+ if (compositeKeys.length && otherContext) {
+ otherContext.setCompositeKeys(compositeKeys);
+ }
+ if (otherContext) {
+ otherContext.fetchRowActionDetails = null;
+ }
+ }
+
+ const presetArray = [];
+ const rawMetadata = pConn$.getRawMetadata().config;
+ const rawPresets = rawMetadata.presets;
+ const isMetaWithPresets = rawPresets && rawPresets.length !== 0;
+ const childrenIterator = isMetaWithPresets ? rawPresets : children;
+ const resolvedPresets = pConn$.getConfigProps().presets;
+ let fields;
+ let tableConfig;
+ childrenIterator?.forEach((presetMeta, index) => {
+ const { configFields } = getTableConfigFromPresetMeta(
+ { ...presetMeta, label: resolvedPresets[index].label },
+ isMetaWithPresets,
+ pConn$,
+ classID,
+ primaryFields,
+ metaFields
+ );
+ const pushToComponentsList = fieldType => {
+ listOfComponents.push(fieldType);
+ };
+ // read report columns response - in case of nonqueryable ignore the response and rely only on the fields configured at authoing time in presets
+ const reportColumnsSet = isQueryable ? getReportColumns(promisesResponseArray[1]) : new Set();
+
+ const configFieldSet = prepareConfigFields(configFields, pushToComponentsList);
+
+ // FIXME #EmbeddedPropertyPatch
+ // TODO: Remove this merge logic when the metadata response includes all optimized embedded page and pagelists
+ // merging configured embedded properties with metadata so that while preparing extra fields, config fields can get other properties from meta(eg: category, displayAsLink)
+ // get configured embedded properties metadata and get new mergedMetaFields
+ const configEmbeddedFieldsMeta = getConfigEmbeddedFieldsMeta(configFieldSet, classID);
+ const mergedMetaFields = mergeConfigEmbeddedFieldsMeta(configEmbeddedFieldsMeta, metaFields);
+
+ const extraFields = prepareExtraFields(
+ mergedMetaFields, // passing new merged meta fields which has meta of configured embedded fields of current preset
+ configFields,
+ configFieldSet,
+ reportColumnsSet,
+ classID,
+ showDynamicFields
+ );
+
+ if (isQueryable) {
+ updatePageFieldsConfig(configFields, classID);
+ preparePropertyMaps([...configFields, ...extraFields], classID, context);
+ }
+
+ const { getMappedProperty } = context;
+
+ fields = initializeColumns([...configFields, ...extraFields], getMappedProperty);
+ const patchQueryFields = preparePatchQueryFields(fields, isDataObject, classID);
+ const itemKey = getItemKey(fields);
+ tableConfig = { fieldDefs: fields, patchQueryFields, itemKey, isQueryable };
+ });
+ const meta = tableConfig;
+ params.listContext = { meta, presets: presetArray, apiContext: { ...apiContext } };
+ return params.listContext;
+};
diff --git a/src/app/_samples/mediaco/mediaCoStyles.scss b/src/app/_samples/mediaco/mediaCoStyles.scss
index 5d3dee83..a5ebffa5 100644
--- a/src/app/_samples/mediaco/mediaCoStyles.scss
+++ b/src/app/_samples/mediaco/mediaCoStyles.scss
@@ -2,7 +2,8 @@
// Action Buttons styles for MediaCo sample app - start
.button-bar {
gap: 1rem;
- margin-top: 1.2rem;
+ margin-top: 2rem;
+ margin-bottom: 0.5rem;
}
.right-group {
@@ -18,10 +19,19 @@
// Self Service Case View styles for MediaCo sample app - start
.psdk-flow-container-top {
background-color: #fff !important;
- padding: 0.5rem 0.625rem !important;
+ padding: 2rem !important;
border-radius: 0 !important;
border-top-left-radius: 2rem !important;
border-bottom: gray 0.0625rem solid !important;
+
+ h2 {
+ color: #46185a;
+ font-weight: 700;
+ font-size: 36px;
+ padding-left: 1rem;
+ padding-top: 0.75rem;
+ margin: 0;
+ }
}
// Self Service Case View styles for MediaCo sample app - end
}