@inizioevoke/veeva-astroclm-core 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1 @@
1
- export * from './const.js';
2
- export { default as loadEnv } from './env.js';
3
- export { default as parseArgv } from './parse-argv.js';
4
- export { default as parseEnv } from './parse-env.js';
5
1
  export * from './utils.js';
package/dist/lib/index.js CHANGED
@@ -1,5 +1,2 @@
1
- export * from './const.js';
2
- export { default as loadEnv } from './env.js';
3
- export { default as parseArgv } from './parse-argv.js';
4
- export { default as parseEnv } from './parse-env.js';
1
+ // only export client-side compatible scripts
5
2
  export * from './utils.js';
@@ -0,0 +1,5 @@
1
+ export * from './const.js';
2
+ export { default as loadEnv } from './env.js';
3
+ export { default as parseArgv } from './parse-argv.js';
4
+ export { default as parseEnv } from './parse-env.js';
5
+ export * from './utils.js';
@@ -0,0 +1,5 @@
1
+ export * from './const.js';
2
+ export { default as loadEnv } from './env.js';
3
+ export { default as parseArgv } from './parse-argv.js';
4
+ export { default as parseEnv } from './parse-env.js';
5
+ export * from './utils.js';
@@ -1,14 +1,18 @@
1
1
  import type { IVeevaConfig } from '../types';
2
+ export interface AdditionalField {
3
+ header: string;
4
+ value?: string;
5
+ property?: string;
6
+ }
2
7
  interface Params {
3
8
  config: IVeevaConfig;
4
9
  deployDir: string;
5
10
  buildName: string;
6
11
  fieldsOnly?: boolean;
7
12
  additional?: {
8
- headers: string[];
9
- presentation: string[];
10
- slides: string[][];
11
- shared: string[];
13
+ presentation: AdditionalField[];
14
+ slides: AdditionalField[];
15
+ shared: AdditionalField[];
12
16
  };
13
17
  }
14
18
  export default function ({ config, deployDir, buildName, fieldsOnly, additional }: Params): Promise<void>;
@@ -4,7 +4,30 @@ import logger from '../lib/logger.js';
4
4
  import { pathToSlideZip } from '../lib/utils.js';
5
5
  export default async function ({ config, deployDir, buildName, fieldsOnly = false, additional }) {
6
6
  logger.info('Creating multichannel loader CSV');
7
- const headers = (fieldsOnly ? CSV_HEADERS_FIELDS_ONLY : CSV_HEADERS).concat(additional?.headers ?? []);
7
+ const headers = fieldsOnly ? CSV_HEADERS_FIELDS_ONLY : CSV_HEADERS;
8
+ additional?.presentation?.forEach((a) => {
9
+ if (a.value || (a.property && config.presentation[a.property])) {
10
+ headers.push(a.header);
11
+ }
12
+ });
13
+ additional?.slides?.forEach((a) => {
14
+ if (a.value) {
15
+ headers.push(a.header);
16
+ }
17
+ else if (a.property && config.presentation.slides) {
18
+ const slide = config.presentation.slides.find((s) => {
19
+ return s[a.property];
20
+ });
21
+ if (slide) {
22
+ headers.push(a.header);
23
+ }
24
+ }
25
+ });
26
+ additional?.shared?.forEach((a) => {
27
+ if (a.value || (a.property && config.presentation.shared && config.presentation.shared[a.property])) {
28
+ headers.push(a.header);
29
+ }
30
+ });
8
31
  const presentation = config.presentation;
9
32
  for (const [key, header] of Object.entries(BINDER_PRES_HEADERS)) {
10
33
  if (presentation[key]) {
@@ -17,12 +40,12 @@ export default async function ({ config, deployDir, buildName, fieldsOnly = fals
17
40
  }
18
41
  }
19
42
  const rows = [headers];
20
- rows.push(createPresentationRow(headers, config).concat(additional?.presentation ?? []));
43
+ rows.push(createPresentationRow(headers, config, additional?.presentation));
21
44
  for (let i = 0; i < config.presentation.slides.length; i++) {
22
- rows.push(createSlideRow(headers, config, config.presentation.slides[i]).concat(additional?.slides?.[i] ?? []));
45
+ rows.push(createSlideRow(headers, config, config.presentation.slides[i], additional?.slides));
23
46
  }
24
- const shared = createSharedRow(headers, config);
25
- shared && rows.push(shared.concat(additional?.shared ?? []));
47
+ const shared = createSharedRow(headers, config, additional?.shared);
48
+ shared && rows.push(shared);
26
49
  await writeFile(join(deployDir, `_${config.presentation.presentationId}_${buildName}.csv`), rows.map((r) => { return r.join(','); }).join('\n'), { encoding: 'utf8' });
27
50
  }
28
51
  const VEEVA_YES = 'YES', VEEVA_NO = 'NO', VEEVA_TYPE_PRESENTATION = 'Presentation', VEEVA_TYPE_SLIDE = 'Slide', VEEVA_TYPE_SHARED = 'Shared', VEEVA_LIFECYCLE_BINDER = 'Binder Lifecycle', VEEVA_LIFECYCLE_CRM_CONTENT = 'CRM Content Lifecycle', VEEVA_IOS_RESOLUTION_DEFAULT = 'Default For Device', VEEVA_MEDIATYPE_HTML = 'HTML',
@@ -30,10 +53,10 @@ const VEEVA_YES = 'YES', VEEVA_NO = 'NO', VEEVA_TYPE_PRESENTATION = 'Presentatio
30
53
  HEADER_FIELDS_ONLY = 'Fields Only', HEADER_EXTERNAL_ID = 'external_id__v', HEADER_NAME = 'name__v', HEADER_TYPE = 'Type', HEADER_LIFECYCLE = 'lifecycle__v', HEADER_PRES_ID = 'pres.crm_presentation_id__v', HEADER_PRES_TITLE = 'pres.title__v', HEADER_PRES_PRODUCT = 'pres.product__v.name__v', HEADER_PRES_COUNTRY = 'pres.country__v.name__v', HEADER_PRES_LANGUAGE = 'pres.language__v', HEADER_PRES_CLM_CONTENT = 'pres.clm_content__v', HEADER_PRES_CLM_HIDDEN = 'pres.crm_hidden__v', HEADER_PRES_TRAINING = 'pres.crm_training__v', HEADER_PRES_CRM_TARGET = 'pres.crm_target_platform__v', HEADER_PRES_CRM_ORG = 'pres.crm_org__v.crm_org_id__v', HEADER_PRES_CRM_PRODUCT = 'pres.crm_product__v.id', // Get the Veeva ID from the URL
31
54
  HEADER_PRES_CRM_CONTENT_GROUP = 'pres.crm_content_group__v.id', // Get the Veeva ID from the URL
32
55
  HEADER_PRES_CRM_DETAIL_GROUP = 'pres.crm_detail_group__v.id', // Get the Veeva ID from the URL
33
- HEADER_PRES_DETAIL_GROUP = 'pres.detail_group__v', HEADER_PRESENTATION = 'Presentation Link', HEADER_SLIDE_TITLE = 'slide.title__v', HEADER_SLIDE_PRODUCT = 'slide.product__v.name__v', HEADER_SLIDE_COUNTRY = 'slide.country__v.name__v', HEADER_SLIDE_LANGUAGE = 'slide.language__v', HEADER_SLIDE_MEDIA_TYPE = 'slide.crm_media_type__v', HEADER_SLIDE_IOS_RESOLUTION = 'slide.ios_resolution__v', HEADER_SLIDE_RELATED_SHARED = 'slide.related_shared_resource__v', HEADER_SLIDE_CLM_CONTENT = 'slide.clm_content__v', HEADER_SLIDE_FILE_NAME = 'slide.filename', HEADER_SLIDE_REACTIONS = 'slide.crm_custom_reaction__v', HEADER_SLIDE_DISABLE_ACTIONS = 'slide.crm_disable_actions__v', HEADER_SLIDE_CRM_TARGET = 'slide.crm_target_platform__v', HEADER_SLIDE_CRM_ORG = 'slide.crm_org__v.crm_org_id__v', HEADER_SLIDE_CRM_PRODUCT = 'slide.crm_product__v.id', // Get the Veeva ID from the URL
56
+ HEADER_PRES_DETAIL_GROUP = 'pres.detail_group__v', HEADER_PRES_EXP_DATE = 'pres.expiration_date__v', HEADER_PRESENTATION = 'Presentation Link', HEADER_SLIDE_TITLE = 'slide.title__v', HEADER_SLIDE_PRODUCT = 'slide.product__v.name__v', HEADER_SLIDE_COUNTRY = 'slide.country__v.name__v', HEADER_SLIDE_LANGUAGE = 'slide.language__v', HEADER_SLIDE_MEDIA_TYPE = 'slide.crm_media_type__v', HEADER_SLIDE_IOS_RESOLUTION = 'slide.ios_resolution__v', HEADER_SLIDE_RELATED_SHARED = 'slide.related_shared_resource__v', HEADER_SLIDE_CLM_CONTENT = 'slide.clm_content__v', HEADER_SLIDE_FILE_NAME = 'slide.filename', HEADER_SLIDE_REACTIONS = 'slide.crm_custom_reaction__v', HEADER_SLIDE_DISABLE_ACTIONS = 'slide.crm_disable_actions__v', HEADER_SLIDE_CRM_TARGET = 'slide.crm_target_platform__v', HEADER_SLIDE_CRM_ORG = 'slide.crm_org__v.crm_org_id__v', HEADER_SLIDE_CRM_PRODUCT = 'slide.crm_product__v.id', // Get the Veeva ID from the URL
34
57
  HEADER_SLIDE_CRM_CONTENT_GROUP = 'slide.crm_content_group__v.id', // Get the Veeva ID from the URL
35
58
  HEADER_SLIDE_CRM_DETAIL_GROUP = 'slide.crm_detail_group__v.id', // Get the Veeva ID from the URL
36
- HEADER_SLIDE_CRM_SHARED = 'slide.crm_shared_resource__v', HEADER_SLIDE_DETAIL_GROUP = 'slide.detail_group__v';
59
+ HEADER_SLIDE_CRM_SHARED = 'slide.crm_shared_resource__v', HEADER_SLIDE_DETAIL_GROUP = 'slide.detail_group__v', HEADER_SLIDE_EXP_DATE = 'slide.expiration_date__v';
37
60
  const CSV_HEADERS = [
38
61
  HEADER_EXTERNAL_ID,
39
62
  HEADER_TYPE,
@@ -71,7 +94,8 @@ const BINDER_PRES_HEADERS = {
71
94
  crmProduct: HEADER_PRES_CRM_PRODUCT,
72
95
  crmContentGroup: HEADER_PRES_CRM_CONTENT_GROUP,
73
96
  crmDetailGroup: HEADER_PRES_CRM_DETAIL_GROUP,
74
- detailGroup: HEADER_PRES_DETAIL_GROUP
97
+ detailGroup: HEADER_PRES_DETAIL_GROUP,
98
+ expirationDate: HEADER_PRES_EXP_DATE
75
99
  };
76
100
  const BINDER_SLIDE_HEADERS = {
77
101
  language: HEADER_SLIDE_LANGUAGE,
@@ -80,9 +104,10 @@ const BINDER_SLIDE_HEADERS = {
80
104
  crmProduct: HEADER_SLIDE_CRM_PRODUCT,
81
105
  crmContentGroup: HEADER_SLIDE_CRM_CONTENT_GROUP,
82
106
  crmDetailGroup: HEADER_SLIDE_CRM_DETAIL_GROUP,
83
- detailGroup: HEADER_SLIDE_DETAIL_GROUP
107
+ detailGroup: HEADER_SLIDE_DETAIL_GROUP,
108
+ expirationDate: HEADER_SLIDE_EXP_DATE
84
109
  };
85
- function createPresentationRow(headers, config) {
110
+ function createPresentationRow(headers, config, additional) {
86
111
  const row = new Array(headers.length).fill('');
87
112
  const presentation = config.presentation;
88
113
  addColumn(headers, row, HEADER_FIELDS_ONLY, VEEVA_YES);
@@ -101,9 +126,12 @@ function createPresentationRow(headers, config) {
101
126
  for (const [key, header] of Object.entries(BINDER_PRES_HEADERS)) {
102
127
  addColumn(headers, row, header, presentation[key]);
103
128
  }
129
+ additional?.forEach((a) => {
130
+ addColumn(headers, row, a.header, a.property ? presentation[a.property] : a.value);
131
+ });
104
132
  return row;
105
133
  }
106
- function createSlideRow(headers, config, slide) {
134
+ function createSlideRow(headers, config, slide, additional) {
107
135
  const row = new Array(headers.length).fill('');
108
136
  addColumn(headers, row, HEADER_FIELDS_ONLY, VEEVA_YES);
109
137
  addColumn(headers, row, HEADER_EXTERNAL_ID, slide.externalId);
@@ -133,9 +161,12 @@ function createSlideRow(headers, config, slide) {
133
161
  for (const [key, header] of Object.entries(BINDER_SLIDE_HEADERS)) {
134
162
  addColumn(headers, row, header, slide[key] ?? config.presentation.slideSettings?.[key] ?? config.slideSettings?.[key]);
135
163
  }
164
+ additional?.forEach((a) => {
165
+ addColumn(headers, row, a.header, a.property ? slide[a.property] : a.value);
166
+ });
136
167
  return row;
137
168
  }
138
- function createSharedRow(headers, config) {
169
+ function createSharedRow(headers, config, additional) {
139
170
  const shared = config.presentation.shared;
140
171
  if (!shared) {
141
172
  return null;
@@ -157,6 +188,9 @@ function createSharedRow(headers, config) {
157
188
  for (const [key, header] of Object.entries(BINDER_SLIDE_HEADERS)) {
158
189
  addColumn(headers, row, header, shared[key] ?? config.presentation.slideSettings?.[key] ?? config.slideSettings?.[key]);
159
190
  }
191
+ additional?.forEach((a) => {
192
+ addColumn(headers, row, a.header, a.property ? shared[a.property] : a.value);
193
+ });
160
194
  return row;
161
195
  }
162
196
  function addColumn(headers, row, header, value) {
@@ -21,6 +21,7 @@ export interface IVeevaClmBinderFields {
21
21
  crmContentGroup?: VeevaIdField | VeevaIdField[];
22
22
  crmDetailGroup?: VeevaIdField;
23
23
  detailGroup?: string;
24
+ expirationDate?: `${string}/${string}/${string}`;
24
25
  }
25
26
 
26
27
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inizioevoke/veeva-astroclm-core",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "",
5
5
  "license": "ISC",
6
6
  "author": "",
@@ -14,6 +14,7 @@
14
14
  "./env": "./dist/env/index.js",
15
15
  "./integrations": "./dist/integrations/index.js",
16
16
  "./lib": "./dist/lib/index.js",
17
+ "./lib/server": "./dist/lib/server.js",
17
18
  "./tasks": "./dist/tasks/index.js",
18
19
  "./types": "./dist/types/index.d.ts"
19
20
  },
package/src/lib/index.ts CHANGED
@@ -1,5 +1,2 @@
1
- export * from './const.js';
2
- export { default as loadEnv } from './env.js';
3
- export { default as parseArgv } from './parse-argv.js';
4
- export { default as parseEnv } from './parse-env.js';
1
+ // only export client-side compatible scripts
5
2
  export * from './utils.js';
@@ -0,0 +1,5 @@
1
+ export * from './const.js';
2
+ export { default as loadEnv } from './env.js';
3
+ export { default as parseArgv } from './parse-argv.js';
4
+ export { default as parseEnv } from './parse-env.js';
5
+ export * from './utils.js';
@@ -4,21 +4,48 @@ import type { IVeevaConfig, IVeevaConfigSlide, Actions, IVeevaClmBinderFields }
4
4
  import logger from '../lib/logger.js';
5
5
  import { pathToSlideZip } from '../lib/utils.js';
6
6
 
7
+ export interface AdditionalField {
8
+ header: string;
9
+ value?: string;
10
+ property?: string;
11
+ }
12
+
7
13
  interface Params {
8
14
  config: IVeevaConfig
9
15
  deployDir: string;
10
16
  buildName: string;
11
17
  fieldsOnly?: boolean;
12
18
  additional?: {
13
- headers: string[];
14
- presentation: string[];
15
- slides: string[][];
16
- shared: string[];
19
+ presentation: AdditionalField[];
20
+ slides: AdditionalField[];
21
+ shared: AdditionalField[];
17
22
  }
18
23
  }
19
24
  export default async function({ config, deployDir, buildName, fieldsOnly = false, additional }: Params) {
20
25
  logger.info('Creating multichannel loader CSV');
21
- const headers: string[] = (fieldsOnly ? CSV_HEADERS_FIELDS_ONLY : CSV_HEADERS).concat(additional?.headers ?? []);
26
+ const headers: string[] = fieldsOnly ? CSV_HEADERS_FIELDS_ONLY : CSV_HEADERS;
27
+ additional?.presentation?.forEach((a) => {
28
+ if (a.value || (a.property && config.presentation[a.property as keyof typeof config.presentation])) {
29
+ headers.push(a.header);
30
+ }
31
+ });
32
+ additional?.slides?.forEach((a) => {
33
+ if (a.value) {
34
+ headers.push(a.header);
35
+ } else if (a.property && config.presentation.slides) {
36
+ const slide = config.presentation.slides.find((s) => {
37
+ return s[a.property as keyof typeof s]
38
+ });
39
+ if (slide) {
40
+ headers.push(a.header);
41
+ }
42
+ }
43
+ });
44
+ additional?.shared?.forEach((a) => {
45
+ if (a.value || (a.property && config.presentation.shared && config.presentation.shared[a.property as keyof typeof config.presentation.shared])) {
46
+ headers.push(a.header);
47
+ }
48
+ });
22
49
  const presentation = config.presentation;
23
50
 
24
51
  for (const [key, header] of Object.entries(BINDER_PRES_HEADERS)) {
@@ -33,12 +60,12 @@ export default async function({ config, deployDir, buildName, fieldsOnly = false
33
60
  }
34
61
 
35
62
  const rows: string[][] = [headers];
36
- rows.push(createPresentationRow(headers, config).concat(additional?.presentation ?? []));
63
+ rows.push(createPresentationRow(headers, config, additional?.presentation));
37
64
  for (let i=0; i<config.presentation.slides.length; i++) {
38
- rows.push(createSlideRow(headers, config, config.presentation.slides[i]).concat(additional?.slides?.[i] ?? []));
65
+ rows.push(createSlideRow(headers, config, config.presentation.slides[i], additional?.slides));
39
66
  }
40
- const shared = createSharedRow(headers, config);
41
- shared && rows.push(shared.concat(additional?.shared ?? []));
67
+ const shared = createSharedRow(headers, config, additional?.shared);
68
+ shared && rows.push(shared);
42
69
 
43
70
  await writeFile(join(deployDir, `_${config.presentation.presentationId}_${buildName}.csv`), rows.map((r)=>{return r.join(',');}).join('\n'), { encoding: 'utf8' });
44
71
  }
@@ -76,6 +103,7 @@ const
76
103
  HEADER_PRES_CRM_CONTENT_GROUP = 'pres.crm_content_group__v.id', // Get the Veeva ID from the URL
77
104
  HEADER_PRES_CRM_DETAIL_GROUP = 'pres.crm_detail_group__v.id', // Get the Veeva ID from the URL
78
105
  HEADER_PRES_DETAIL_GROUP = 'pres.detail_group__v',
106
+ HEADER_PRES_EXP_DATE = 'pres.expiration_date__v',
79
107
 
80
108
  HEADER_PRESENTATION = 'Presentation Link',
81
109
  HEADER_SLIDE_TITLE = 'slide.title__v',
@@ -95,7 +123,8 @@ const
95
123
  HEADER_SLIDE_CRM_CONTENT_GROUP = 'slide.crm_content_group__v.id', // Get the Veeva ID from the URL
96
124
  HEADER_SLIDE_CRM_DETAIL_GROUP = 'slide.crm_detail_group__v.id', // Get the Veeva ID from the URL
97
125
  HEADER_SLIDE_CRM_SHARED = 'slide.crm_shared_resource__v',
98
- HEADER_SLIDE_DETAIL_GROUP = 'slide.detail_group__v';
126
+ HEADER_SLIDE_DETAIL_GROUP = 'slide.detail_group__v',
127
+ HEADER_SLIDE_EXP_DATE = 'slide.expiration_date__v';
99
128
 
100
129
  const CSV_HEADERS = [
101
130
  HEADER_EXTERNAL_ID,
@@ -136,7 +165,8 @@ const BINDER_PRES_HEADERS: Record<keyof IVeevaClmBinderFields, string> = {
136
165
  crmProduct: HEADER_PRES_CRM_PRODUCT,
137
166
  crmContentGroup: HEADER_PRES_CRM_CONTENT_GROUP,
138
167
  crmDetailGroup: HEADER_PRES_CRM_DETAIL_GROUP,
139
- detailGroup: HEADER_PRES_DETAIL_GROUP
168
+ detailGroup: HEADER_PRES_DETAIL_GROUP,
169
+ expirationDate: HEADER_PRES_EXP_DATE
140
170
  };
141
171
  const BINDER_SLIDE_HEADERS: Record<keyof IVeevaClmBinderFields, string> = {
142
172
  language: HEADER_SLIDE_LANGUAGE,
@@ -145,11 +175,12 @@ const BINDER_SLIDE_HEADERS: Record<keyof IVeevaClmBinderFields, string> = {
145
175
  crmProduct: HEADER_SLIDE_CRM_PRODUCT,
146
176
  crmContentGroup: HEADER_SLIDE_CRM_CONTENT_GROUP,
147
177
  crmDetailGroup: HEADER_SLIDE_CRM_DETAIL_GROUP,
148
- detailGroup: HEADER_SLIDE_DETAIL_GROUP
178
+ detailGroup: HEADER_SLIDE_DETAIL_GROUP,
179
+ expirationDate: HEADER_SLIDE_EXP_DATE
149
180
  };
150
181
 
151
182
 
152
- function createPresentationRow(headers: string[], config: IVeevaConfig): string[] {
183
+ function createPresentationRow(headers: string[], config: IVeevaConfig, additional?: AdditionalField[]): string[] {
153
184
  const row: string[] = new Array(headers.length).fill('');
154
185
  const presentation = config.presentation;
155
186
  addColumn(headers, row, HEADER_FIELDS_ONLY, VEEVA_YES);
@@ -168,10 +199,13 @@ function createPresentationRow(headers: string[], config: IVeevaConfig): string[
168
199
  for (const [key, header] of Object.entries(BINDER_PRES_HEADERS)) {
169
200
  addColumn(headers, row, header, presentation[key as keyof IVeevaClmBinderFields]);
170
201
  }
202
+ additional?.forEach((a) => {
203
+ addColumn(headers, row, a.header, a.property ? presentation[a.property as keyof typeof presentation] : a.value)
204
+ });
171
205
  return row;
172
206
  }
173
207
 
174
- function createSlideRow(headers: string[], config: IVeevaConfig, slide: IVeevaConfigSlide): string[] {
208
+ function createSlideRow(headers: string[], config: IVeevaConfig, slide: IVeevaConfigSlide, additional?: AdditionalField[]): string[] {
175
209
  const row: string[] = new Array(headers.length).fill('');
176
210
  addColumn(headers, row, HEADER_FIELDS_ONLY, VEEVA_YES);
177
211
  addColumn(headers, row, HEADER_EXTERNAL_ID, slide.externalId);
@@ -206,10 +240,13 @@ function createSlideRow(headers: string[], config: IVeevaConfig, slide: IVeevaCo
206
240
  for (const [key, header] of Object.entries(BINDER_SLIDE_HEADERS)) {
207
241
  addColumn(headers, row, header, slide[key as keyof IVeevaClmBinderFields] ?? config.presentation.slideSettings?.[key as keyof IVeevaClmBinderFields] ?? config.slideSettings?.[key as keyof IVeevaClmBinderFields]);
208
242
  }
243
+ additional?.forEach((a) => {
244
+ addColumn(headers, row, a.header, a.property ? slide[a.property as keyof typeof slide] : a.value)
245
+ });
209
246
  return row;
210
247
  }
211
248
 
212
- function createSharedRow(headers: string[], config: IVeevaConfig): string[] | null {
249
+ function createSharedRow(headers: string[], config: IVeevaConfig, additional?: AdditionalField[]): string[] | null {
213
250
  const shared = config.presentation.shared;
214
251
  if (!shared) {
215
252
  return null;
@@ -231,6 +268,9 @@ function createSharedRow(headers: string[], config: IVeevaConfig): string[] | nu
231
268
  for (const [key, header] of Object.entries(BINDER_SLIDE_HEADERS)) {
232
269
  addColumn(headers, row, header, shared[key as keyof IVeevaClmBinderFields] ?? config.presentation.slideSettings?.[key as keyof IVeevaClmBinderFields] ?? config.slideSettings?.[key as keyof IVeevaClmBinderFields]);
233
270
  }
271
+ additional?.forEach((a) => {
272
+ addColumn(headers, row, a.header, a.property ? shared[a.property as keyof typeof shared] : a.value)
273
+ });
234
274
  return row;
235
275
  }
236
276
 
@@ -21,6 +21,7 @@ export interface IVeevaClmBinderFields {
21
21
  crmContentGroup?: VeevaIdField | VeevaIdField[];
22
22
  crmDetailGroup?: VeevaIdField;
23
23
  detailGroup?: string;
24
+ expirationDate?: `${string}/${string}/${string}`;
24
25
  }
25
26
 
26
27