diff --git a/404.html b/404.html index d76145533..cf7ef95eb 100644 --- a/404.html +++ b/404.html @@ -10,13 +10,13 @@ - +
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- + \ No newline at end of file diff --git a/assets/js/937990e1.a96e3384.js b/assets/js/937990e1.dc9c0439.js similarity index 67% rename from assets/js/937990e1.a96e3384.js rename to assets/js/937990e1.dc9c0439.js index 48d76e633..464b4db26 100644 --- a/assets/js/937990e1.a96e3384.js +++ b/assets/js/937990e1.dc9c0439.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmd=self.webpackChunkmd||[]).push([[7531],{4137:(e,t,a)=>{a.d(t,{Zo:()=>u,kt:()=>h});var n=a(7294);function r(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function o(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function i(e){for(var t=1;t=0||(r[a]=e[a]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(r[a]=e[a])}return r}var s=n.createContext({}),p=function(e){var t=n.useContext(s),a=t;return e&&(a="function"==typeof e?e(t):i(i({},t),e)),a},u=function(e){var t=p(e.components);return n.createElement(s.Provider,{value:t},e.children)},d="mdxType",c={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var a=e.components,r=e.mdxType,o=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),d=p(a),m=r,h=d["".concat(s,".").concat(m)]||d[m]||c[m]||o;return a?n.createElement(h,i(i({ref:t},u),{},{components:a})):n.createElement(h,i({ref:t},u))}));function h(e,t){var a=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=a.length,i=new Array(o);i[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[d]="string"==typeof e?e:r,i[1]=l;for(var p=2;p{a.d(t,{m:()=>o});var n=a(7294);const r="leadText_qzwo",o=e=>{let{content:t}=e;return n.createElement(n.Fragment,null,n.createElement("p",{id:r},t))}},5196:(e,t,a)=>{a.r(t),a.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>m,frontMatter:()=>i,metadata:()=>s,toc:()=>u});var n=a(7462),r=(a(7294),a(4137)),o=a(1872);const i={id:"mcp-serverside-code-properties",title:"MCP Serverside Code Properties",sidebar_label:"Serverside Code Properties",description:"Build your marketers' dream campaign configuration UI in Marketing Cloud Personalization (Interaction Studio). All the input magic in one place.",image:"img/og/og-image-mcp-serverside-code-properties.png",tags:["Marketing Cloud","Marketing Cloud Personalization","Interaction Studio","Personalisation","TypeScript"]},l=void 0,s={unversionedId:"interaction-studio/mcp-serverside-code-properties",id:"interaction-studio/mcp-serverside-code-properties",title:"MCP Serverside Code Properties",description:"Build your marketers' dream campaign configuration UI in Marketing Cloud Personalization (Interaction Studio). All the input magic in one place.",source:"@site/docs/interaction-studio/mcp-serverside-code-properties.mdx",sourceDirName:"interaction-studio",slug:"/interaction-studio/mcp-serverside-code-properties",permalink:"/docs/interaction-studio/mcp-serverside-code-properties",draft:!1,editUrl:"https://github.com/MateuszDabrowski/mateuszdabrowski.pl/edit/master/docs/interaction-studio/mcp-serverside-code-properties.mdx",tags:[{label:"Marketing Cloud",permalink:"/docs/tags/marketing-cloud"},{label:"Marketing Cloud Personalization",permalink:"/docs/tags/marketing-cloud-personalization"},{label:"Interaction Studio",permalink:"/docs/tags/interaction-studio"},{label:"Personalisation",permalink:"/docs/tags/personalisation"},{label:"TypeScript",permalink:"/docs/tags/type-script"}],version:"current",lastUpdatedBy:"Mateusz D\u0105browski",lastUpdatedAt:1697495003,formattedLastUpdatedAt:"Oct 16, 2023",frontMatter:{id:"mcp-serverside-code-properties",title:"MCP Serverside Code Properties",sidebar_label:"Serverside Code Properties",description:"Build your marketers' dream campaign configuration UI in Marketing Cloud Personalization (Interaction Studio). All the input magic in one place.",image:"img/og/og-image-mcp-serverside-code-properties.png",tags:["Marketing Cloud","Marketing Cloud Personalization","Interaction Studio","Personalisation","TypeScript"]},sidebar:"docs",previous:{title:"Serverside Code Basics",permalink:"/docs/interaction-studio/mcp-serverside-code-basics"}},p={},u=[{value:"Basic properties",id:"basic-properties",level:2},{value:"Boolean",id:"boolean",level:3},{value:"String",id:"string",level:3},{value:"\u203a Array String",id:"-array-string",level:4},{value:"\u203a Default String",id:"-default-string",level:4},{value:"\u203a Select String",id:"-select-string",level:4},{value:"\u203a Rich Text String",id:"-rich-text-string",level:4},{value:"Number",id:"number",level:3},{value:"\u203a Array Number",id:"-array-number",level:4},{value:"\u203a Default Number",id:"-default-number",level:4},{value:"\u203a Select Number",id:"-select-number",level:4},{value:"Color",id:"color",level:3},{value:"\u203a Default Color",id:"-default-color",level:4},{value:"Datetime",id:"datetime",level:3},{value:"\u203a Datetime Range",id:"-datetime-range",level:4},{value:"Readonly property",id:"readonly-property",level:2},{value:"Complex property",id:"complex-property",level:2},{value:"Complex default values",id:"complex-default-values",level:3},{value:"Decorators",id:"decorators",level:2},{value:"@title & @subtitle",id:"title--subtitle",level:3},{value:"@markdown",id:"markdown",level:3},{value:"@header & @headerSubtitle",id:"header--headersubtitle",level:3},{value:"@hidden & @shownIf",id:"hidden--shownif",level:3},{value:"@buttonGroup",id:"buttongroup",level:3},{value:"@optional",id:"optional",level:3}],d={toc:u},c="wrapper";function m(e){let{components:t,...a}=e;return(0,r.kt)(c,(0,n.Z)({},d,a,{components:t,mdxType:"MDXLayout"}),(0,r.kt)(o.m,{content:"Build your marketers' dream campaign configuration UI in Marketing Cloud Personalization (Interaction Studio). All the input magic in one place.",mdxType:"LeadText"}),(0,r.kt)("p",null,"Once you learn the ",(0,r.kt)("a",{parentName:"p",href:"/docs/interaction-studio/mcp-serverside-code-basics"},"very basics of Serverside Code")," in Marketing Cloud Personalization (Interaction Studio), it is time to start writing code. The easiest way is, to begin with creating the campaign creation UI for the marketer - defining all the inputs they will need to fill in to drive the experience. It will be our focus in this article."),(0,r.kt)("p",null,"MCP Serverside Code offers five basic property data types: ",(0,r.kt)("a",{parentName:"p",href:"#boolean"},"Boolean"),", ",(0,r.kt)("a",{parentName:"p",href:"#string"},"String"),", ",(0,r.kt)("a",{parentName:"p",href:"#number"},"Number"),", ",(0,r.kt)("a",{parentName:"p",href:"#color"},"Color")," and ",(0,r.kt)("a",{parentName:"p",href:"#datime"},"DateTime"),". Those, their combinations into ",(0,r.kt)("a",{parentName:"p",href:"#complex-property"},"complex properties")," and modifications through ",(0,r.kt)("a",{parentName:"p",href:"#decorators"},"decorators")," enable you to build nearly anything you need for your campaign configuration screen."),(0,r.kt)("p",null,"Let's dive into how you can make the most of those features."),(0,r.kt)("h2",{id:"basic-properties"},"Basic properties"),(0,r.kt)("p",null,"Basic properties are the base building blocks of your campaign configuration. They are all you need to make a perfect campaign template, and mastering them will be essential to doing more complex UIs with ",(0,r.kt)("a",{parentName:"p",href:"#complex-property"},"complex properties")," and ",(0,r.kt)("a",{parentName:"p",href:"#decorators"},"decorators"),"."),(0,r.kt)("h3",{id:"boolean"},"Boolean"),(0,r.kt)("p",null,"The boolean property lets you capture a ",(0,r.kt)("inlineCode",{parentName:"p"},"true")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"false")," flag by creating a checkbox input:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Create simple checkbox"',title:'"Create',simple:!0,'checkbox"':!0},"isTextLeftToRight: boolean;\n// Input: Editable unchecked checkbox\n// Output: true or false\n")),(0,r.kt)("p",null,"The boolean property is unchecked by default (returns ",(0,r.kt)("inlineCode",{parentName:"p"},"false"),"), but you can change it by passing a ",(0,r.kt)("inlineCode",{parentName:"p"},"true")," value in the code:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Checkbox checked by default"',title:'"Checkbox',checked:!0,by:!0,'default"':!0},"isTextLeftToRight: boolean = true;\n// Input: Editable checked checkbox\n// Output: true or false\n")),(0,r.kt)("p",null,"Boolean fields are crucial for template development as they are perfect for building logic, for example, with the ",(0,r.kt)("a",{parentName:"p",href:"#hidden--shownif"},(0,r.kt)("inlineCode",{parentName:"a"},"@shownIf")," decorator"),"."),(0,r.kt)("h3",{id:"string"},"String"),(0,r.kt)("p",null,"String property lets you capture a text input:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Create empty text input field"',title:'"Create',empty:!0,text:!0,input:!0,'field"':!0},'content: string;\n// Input: Empty text input\n// Output: "Provided text"\n')),(0,r.kt)("p",null,"However, there is much more to string property than just that."),(0,r.kt)("h4",{id:"-array-string"},"\u203a Array String"),(0,r.kt)("p",null,"You may want to capture more than one string. Easy, you can do it with two characters:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Capture multiple strings"',title:'"Capture',multiple:!0,'strings"':!0},'hashtags: string[];\n// Input: Empty text input with an option for converting entered text into one of the values\n// Output: ["Array", "of", "Strings"]\n')),(0,r.kt)("p",null,"By adding ",(0,r.kt)("inlineCode",{parentName:"p"},"[]")," after the type definition, we convert the field to an array of strings. During configuration, you can provide multiple values that, in the payload, will be returned as an array. It doesn't accept duplicate values."),(0,r.kt)("h4",{id:"-default-string"},"\u203a Default String"),(0,r.kt)("p",null,"To simplify the work for your marketers or to provide recommended examples of expected data, you can easily add a default value to your string by delivering it as a string after the equal sign:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Add default content"',title:'"Add',default:!0,'content"':!0},"content: string = 'This is default content';\n// Input: Editable prefilled text input\n// Output: \"Provided text\"\n")),(0,r.kt)("p",null,"It can be freely edited during configuration but will be passed as-is to the payload if no changes are made."),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"You can provide default values also for string arrays:"),(0,r.kt)("pre",{parentName:"admonition"},(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Default values for array string"',title:'"Default',values:!0,for:!0,array:!0,'string"':!0},'hashtags: string[] = [\'interaction-studio\', \'marketing-cloud-personalization\'];\n// Input: Editable prefilled text input with two deletable values and space to write additional ones\n// Output: ["Array", "of", "Strings"]\n'))),(0,r.kt)("h4",{id:"-select-string"},"\u203a Select String"),(0,r.kt)("p",null,"You can go one step further and lock the string property to only a set of preconfigured values using the ",(0,r.kt)("inlineCode",{parentName:"p"},"@options")," decorator. It is excellent when you need a particular value, for example, for a programmatic logic in other parts of your template."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Limit possible values with select"',title:'"Limit',possible:!0,values:!0,with:!0,'select"':!0},"@options(['brandA', 'brandB', 'brandC'])\nbrand: string;\n// Input: Empty text input showing dropdown with configured options on click\n// Output: \"Selected text\"\n")),(0,r.kt)("p",null,"With such code, the campaign configuration will display a picklist with the hardcoded values. It will output a string to the campaign payload."),(0,r.kt)("p",null,"You can also provide ",(0,r.kt)("a",{parentName:"p",href:"#-default-string"},"default value")," for your select field:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Provide default select value"',title:'"Provide',default:!0,select:!0,'value"':!0},"@options(['brandA', 'brandB', 'brandC'])\nbrand: string = 'brandA';\n// Input: Editable prefilled text input showing dropdown with configured options on click\n// Output: \"Selected text\"\n")),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"You can also create a select field without the decorator using literals:"),(0,r.kt)("pre",{parentName:"admonition"},(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Literal select field"',title:'"Literal',select:!0,'field"':!0},"brand: 'brandA' | 'brandB' | 'brandC';\n// Input: Empty text input showing dropdown with configured options on click\n// Output: \"Selected text\"\n"))),(0,r.kt)("p",null,"Just as you can capture an array of text inputs, you can do the same for select strings and create a multi-picklist. However, while the preconfigured options will be visible and accessible to pick, new options - outside of what you hardcoded - ",(0,r.kt)("strong",{parentName:"p"},"can")," be freely added."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Multi-picklist"',title:'"Multi-picklist"'},"@options(['brandA', 'brandB', 'brandC'])\nbrands: string[];\n// Input: Empty text field with picklist and option to create new values. Allows for multiple additions.\n// Output: [\"Array\", \"of\", \"Strings\"]\n")),(0,r.kt)("p",null,"You can also provide ",(0,r.kt)("a",{parentName:"p",href:"#-default-string"},"default value")," for your multi-select field:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Provide default multi-select values"',title:'"Provide',default:!0,"multi-select":!0,'values"':!0},"@options(['brandA', 'brandB', 'brandC'])\nbrands: string[] = ['brandA', 'brandB'];\n// Input: Editable prefilled text field with picklist and option to create new values. Allows for multiple additions.\n// Output: [\"Array\", \"of\", \"Strings\"]\n")),(0,r.kt)("p",null,"It will output an array of strings to the campaign payload."),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"You can also create a multi-select field without the decorator using literals:"),(0,r.kt)("pre",{parentName:"admonition"},(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Literal multi-select field"',title:'"Literal',"multi-select":!0,'field"':!0},"brand: ('brandA' | 'brandB' | 'brandC')[];\n// Input: Empty text field with picklist and option to create new values. Allows for multiple additions.\n// Output: [\"Array\", \"of\", \"Strings\"]\n"))),(0,r.kt)("h4",{id:"-rich-text-string"},"\u203a Rich Text String"),(0,r.kt)("p",null,"You can easily convert this simple text input into a rich text field by using a ",(0,r.kt)("inlineCode",{parentName:"p"},"@richText")," decorator:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Capture styling with a single decorator"',title:'"Capture',styling:!0,with:!0,a:!0,single:!0,'decorator"':!0},"@richText(true)\ncontent: string;\n// Input: Text input that adapts to the content size and provides bold, italic, underscore and link WYSIWYG options.\n// Output: \"String with optionalHTML
Includes multiline\"\n")),(0,r.kt)("p",null,"This decorator will change the input field to a multiline box with bold, italic, underscore and link features. The payload will return those styles in the form of HTML that can then be used directly, for example, in the Handlebars tab of Web Campaign."),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"While the rich text options are minimal, you can write some other HTML (for example, ",(0,r.kt)("inlineCode",{parentName:"p"},"")," with style attribute) directly in the box, which will be passed to the output correctly. It will even display as a formatted text after you revisit the configuration.")),(0,r.kt)("h3",{id:"number"},"Number"),(0,r.kt)("p",null,"Number property lets you capture integer (",(0,r.kt)("inlineCode",{parentName:"p"},"3"),") and decimal (",(0,r.kt)("inlineCode",{parentName:"p"},"3.14"),") values:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Create empty numeric input"',title:'"Create',empty:!0,numeric:!0,'input"':!0},"recommendationsCount: number;\n// Input: Empty text (sic!) input\n// Output: 3.14\n")),(0,r.kt)("p",null,"Keep in mind that non-numeric values might break the campaign without any error visible in the configuration screen. You can either display a warning using ",(0,r.kt)("a",{parentName:"p",href:"#hidden--shownif"},(0,r.kt)("inlineCode",{parentName:"a"},"@shownIf")," decorator")," and a ",(0,r.kt)("a",{parentName:"p",href:"#string"},"string field")," or create sanitization logic in the run block."),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"You can use ",(0,r.kt)("inlineCode",{parentName:"p"},"@unit")," ",(0,r.kt)("a",{parentName:"p",href:"#decorators"},"decorator")," to provide a unit description next to the input. It has no impact on the outputted value but gives marketer information about the expected value:"),(0,r.kt)("pre",{parentName:"admonition"},(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{1} title="Unit decorator on number"',"{1}":!0,title:'"Unit',decorator:!0,on:!0,'number"':!0},"@unit('ms')\ndelayBeforeDisplay: number;\n"))),(0,r.kt)("h4",{id:"-array-number"},"\u203a Array Number"),(0,r.kt)("p",null,"Just as with strings, you can capture multiple numeric values by adding ",(0,r.kt)("inlineCode",{parentName:"p"},"[]"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Capture multiple numbers"',title:'"Capture',multiple:!0,'numbers"':!0},"productIds: number[];\n// Input: A plus icon that adds new text inputs with delete buttons\n// Output: [3.14, 3, 5]\n")),(0,r.kt)("p",null,"It will output an array of numbers to the campaign payload."),(0,r.kt)("h4",{id:"-default-number"},"\u203a Default Number"),(0,r.kt)("p",null,"Again, just as with strings, you can provide an editable default value:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Default numeric value"',title:'"Default',numeric:!0,'value"':!0},"recommendationsCount: number = 6;\n// Input: Editable prefilled text input\n// Output: 6\n")),(0,r.kt)("p",null,"The same can be done for Array Numbers:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Default numeric array value"',title:'"Default',numeric:!0,array:!0,'value"':!0},"productIds: number[] = [123, 456, 789];\n// Input: Editable and deletable prefilled text inputs and a plus icon that can add more\n// Output: [123, 456, 32]\n\n")),(0,r.kt)("h4",{id:"-select-number"},"\u203a Select Number"),(0,r.kt)("p",null,"As with ",(0,r.kt)("a",{parentName:"p",href:"#-select-string"},"strings"),", you can create a numeric picklist:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Numeric picklist"',title:'"Numeric','picklist"':!0},"recommendationsCount: 1 | 3 | 6 | 9;\n// Input: Empty text input showing dropdown with configured options on click\n// Output: 1\n")),(0,r.kt)("p",null,"You can also pass a default value:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Numeric picklist with default value"',title:'"Numeric',picklist:!0,with:!0,default:!0,'value"':!0},"recommendationsCount: 1 | 3 | 6 | 9 = 6;\n// Input: Editable prefilled text input showing dropdown with configured options on click\n// Output: 6\n")),(0,r.kt)("p",null,"You can also create a numeric multi-picklist:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Numeric multi-picklist"',title:'"Numeric','multi-picklist"':!0},"orderOfSections: (1 | 2 | 3 | 4 | 5)[];\n// Input: Empty text field with picklist. Allows for multiple additions.\n// Output: [1, 3, 4, 2, 5]\n")),(0,r.kt)("p",null,"Different from ",(0,r.kt)("a",{parentName:"p",href:"#-select-string"},"strings multi-select"),", with a numeric multi-picklist, the marketer won't be able to add new options outside of what you preconfigured. Yay!"),(0,r.kt)("p",null,"However, there are three caveats to keep in mind:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"You cannot have ",(0,r.kt)("inlineCode",{parentName:"li"},"0")," as one of the options. The picklist will crash."),(0,r.kt)("li",{parentName:"ol"},"You cannot use ",(0,r.kt)("inlineCode",{parentName:"li"},"@options")," to create the picklist. It will be ignored."),(0,r.kt)("li",{parentName:"ol"},"The single-select picklist will always cut the visibility of the last digit in the longest option after selecting it.")),(0,r.kt)("h3",{id:"color"},"Color"),(0,r.kt)("p",null,"Color property lets you create a color picker with a single line of code:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Create color picker"',title:'"Create',color:!0,'picker"':!0},'backgroundColor: Color;\n// Input: An input pseudo-prefilled with #FFFFFF and a color box that, on click, opens the color selection interface\n// Output: {\n// "hex": "#da4e55",\n// "r": 218,\n// "g": 78,\n// "b": 85,\n// "a": 1\n//}\n')),(0,r.kt)("p",null,"You will be able to select the color by dragging the selector over a colour palette or by providing hex/rgba/hsl values. The property will output to payload a ",(0,r.kt)("inlineCode",{parentName:"p"},"color")," object with both hex and rgba values."),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"While the property will look like it is prefilled with white color, it will output 'null' until you pick a color in the interface. If you want the white to be a working default option, you must configure it explicitly.")),(0,r.kt)("h4",{id:"-default-color"},"\u203a Default Color"),(0,r.kt)("p",null,"You can provide a default color, but it will be more complex. You cannot just give a single hex value; you need to replicate the whole output object:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title"Default color value"','title"Default':!0,color:!0,'value"':!0},'backgroundColor: Color = {\n "hex": "#da4e55",\n "r": 218,\n "g": 78,\n "b": 85,\n "a": 1\n};\n// Input: An input truly prefilled with #FFFFFF and a color box that, on click, opens the color selection interface\n// Output: {\n// "hex": "#da4e55",\n// "r": 218,\n// "g": 78,\n// "b": 85,\n// "a": 1\n//}\n')),(0,r.kt)("h3",{id:"datetime"},"Datetime"),(0,r.kt)("p",null,"Datetime property provides a clean-looking calendar widget that lets the marketer easily pick up a date."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Create datetime picker"',title:'"Create',datetime:!0,'picker"':!0},'promotionDate: DateTime;\n// Input: Empty input with calendar icon that, on click, opens full calendar for date selection\n// Output: {\n// "dateTime": [\n// "2023-10-01T16:00:00.000Z"\n// ]\n//}\n')),(0,r.kt)("p",null,"There are two payload-related things to consider:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"The DateTime property always outputs an object with a single ",(0,r.kt)("inlineCode",{parentName:"li"},"dateTime")," property assigned to an array of strings."),(0,r.kt)("li",{parentName:"ol"},"The date-times are returned as ISO 8601 strings (",(0,r.kt)("inlineCode",{parentName:"li"},"2023-10-01T16:00:00.000Z"),").")),(0,r.kt)("h4",{id:"-datetime-range"},"\u203a Datetime Range"),(0,r.kt)("p",null,"While you cannot create a multi-select datetime field, you can use the ",(0,r.kt)("inlineCode",{parentName:"p"},"@range")," decorator to select two dates within a nice UI."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Create range datetime picker"',title:'"Create',range:!0,datetime:!0,'picker"':!0},'@range(true)\npromotionDate: DateTime;\n// Input: Empty input with calendar icon that on click opens full calendar for two dates selection with range indicator\n// Output: {\n// "dateTime": [\n// "2023-10-01T16:00:00.000Z",\n// "2023-10-03T16:00:00.000Z"\n// ]\n//}\n')),(0,r.kt)("p",null,"The output will return both selected dates in the ",(0,r.kt)("inlineCode",{parentName:"p"},"dateTime")," array of strings."),(0,r.kt)("hr",null),(0,r.kt)("h2",{id:"readonly-property"},"Readonly property"),(0,r.kt)("p",null,"Apart from ",(0,r.kt)("a",{parentName:"p",href:"#string"},"basic string property")," there is also a very similar readonly string property:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Static readonly property"',title:'"Static',readonly:!0,'property"':!0},"readonly templateVersion = \"Version 1.1\"\n// Input: No input, just a readonly text written in the form\n// Output: 'Version 1.1'\n")),(0,r.kt)("p",null,"As you can see, the key difference is the ",(0,r.kt)("inlineCode",{parentName:"p"},"readonly")," prefix before defining the property. It also uses the ",(0,r.kt)("a",{parentName:"p",href:"#-default-string"},"default string")," approach to assign the value."),(0,r.kt)("p",null,"However, readonly property has one superpower. You can assign an arrow function to it (as long as it will return a string) and use values from other properties to transform them:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{2,5-13} title="Dynamic readonly property"',"{2,5-13}":!0,title:'"Dynamic',readonly:!0,'property"':!0},"header: string = 'DEFault heaDER';\nreadonly upperCaseHeader = () => this?.header.toUpperCase || '';\n// Input: No input, just a readonly text updating real-time based on header input\n// Output: 'DEFAULT HEADER'\nreadonly titleCaseHeader = () => {\n return this?.header\n .toLowerCase()\n .split(' ')\n .filter(word => word !== '')\n .map(word => word.replace(word[0], word[0].toUpperCase()))\n .join(' ')\n || '';\n}\n// Input: No input, just a readonly text updating real-time based on header input\n// Output: 'Default Header'\n")),(0,r.kt)("p",null,"With the dynamic readonly property, you can transform string inputs, concatenate multiple inputs into one field or even create a logic based on non-string inputs (as long as the output is a string)."),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"You can do the same things later in the ",(0,r.kt)("a",{parentName:"p",href:"/docs/interaction-studio/mcp-serverside-code-basics#2-run-block"},"run() block"),". It will be even more powerful there, as you can use the context object. However, you must manually add those calculated values to the ",(0,r.kt)("a",{parentName:"p",href:"/docs/interaction-studio/mcp-serverside-code-basics#3-return-statement"},"return statement")," to see it in the payload."),(0,r.kt)("p",{parentName:"admonition"},"For simple use cases, the readonly property will be more straightforward and provide a nifty real-time preview of the calculated value for the marketer.")),(0,r.kt)("hr",null),(0,r.kt)("h2",{id:"complex-property"},"Complex property"),(0,r.kt)("p",null,"The input configuration fun starts when you combine ",(0,r.kt)("a",{parentName:"p",href:"#basic-properties"},"basic properties")," into complex ones. The method is straightforward. Outside the main class that implements CampaignTemplateComponent, create a new class export that contains all the basic inputs you need:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Define complex property"',title:'"Define',complex:!0,'property"':!0},"export class TimeframedColorPicker {\n @range(true)\n timeframe: DateTime;\n\n color: Color;\n}\n")),(0,r.kt)("p",null,"With complex property defined, you can leverage it in the main class the same way as you do with basic properties:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Use complex property"',title:'"Use',complex:!0,'property"':!0},'timeframedColor: TimeframedColorPicker;\n// Input: Set of basic properties - datetime range and color pickers, in this case\n// Output: {\n// timeframedColor: {\n// timeframe: "dateTime": [\n// "2023-10-01T16:00:00.000Z",\n// "2023-10-03T16:00:00.000Z"\n// ],\n// color: {\n// "hex": "#da4e55",\n// "r": 218,\n// "g": 78,\n// "b": 85,\n// "a": 1\n// }\n// }\n//}\n')),(0,r.kt)("p",null,"What are the benefits of this approach?"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"You can define your complex property class once and then use it multiple times in your form."),(0,r.kt)("li",{parentName:"ol"},"You can control the structure of the Serverside payload.")),(0,r.kt)("p",null,"A complex property creates a new nested object assigned to an input property. Let's look at the difference:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Payload using basic properties"',title:'"Payload',using:!0,basic:!0,'properties"':!0},'@range(true)\ntimeframe: Date;\n\ncolor: Color;\n// Input: Two basic properties - datetime range and color pickers, in this case\n// Output: {\n// timeframe: "dateTime": [\n// "2023-10-01T16:00:00.000Z",\n// "2023-10-03T16:00:00.000Z"\n// ],\n// color: {\n// "hex": "#da4e55",\n// "r": 218,\n// "g": 78,\n// "b": 85,\n// "a": 1\n// }\n//}\n')),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{4,16} title="Payload using complex property"',"{4,16}":!0,title:'"Payload',using:!0,complex:!0,'property"':!0},'timeframedColor: TimeframedColorPicker;\n// Input: Set of basic properties - datetime range and color pickers in this case\n// Output: {\n// timeframedColor: {\n// timeframe: "dateTime": [\n// "2023-10-01T16:00:00.000Z",\n// "2023-10-03T16:00:00.000Z"\n// ],\n// color: {\n// "hex": "#da4e55",\n// "r": 218,\n// "g": 78,\n// "b": 85,\n// "a": 1\n// }\n// }\n//}\n')),(0,r.kt)("p",null,"As you can see, data from basic inputs is assigned as properties to the complex property. This can help with payload readability and might be crucial when adapting your payload to specific requirements (for example, schema expected by a React website or 3rd party system)."),(0,r.kt)("ol",{start:3},(0,r.kt)("li",{parentName:"ol"},"You can nest complex properties")),(0,r.kt)("p",null,"Just as you can add a complex property to a field, you can also create a complex property using complex properties. While it can again give you all the benefits mentioned here, be careful not to go overboard. Deep nesting is more challenging to understand and use. There is no perfect rule, but check out ",(0,r.kt)("a",{parentName:"p",href:"../zen-of-sfmc/#simple-is-better-than-complex"},"Simple/Complex recommendations in Zen of SFMC"),"."),(0,r.kt)("ol",{start:4},(0,r.kt)("li",{parentName:"ol"},"You can leverage it to build an Array of complex properties!")),(0,r.kt)("p",null,"This is the real game changer - out of the basic properties, only ",(0,r.kt)("a",{parentName:"p",href:"#-array-string"},"Strings")," and ",(0,r.kt)("a",{parentName:"p",href:"#-array-string"},"Numbers")," can be used as arrays. But using a complex one, you can also leverage Boolean, Color and Datetime. The approach is the same as previously - just add ",(0,r.kt)("inlineCode",{parentName:"p"},"[]"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{4,16} title="Array of complex properties"',"{4,16}":!0,title:'"Array',of:!0,complex:!0,'properties"':!0},'timeframedColor: TimeframedColorPicker[];\n// Input: A plus icon that adds new sets of basic properties with delete buttons\n// Output: {\n// timeframedColor: [{\n// timeframe: "dateTime": [\n// "2023-10-01T16:00:00.000Z",\n// "2023-10-03T16:00:00.000Z"\n// ],\n// color: {\n// "hex": "#da4e55",\n// "r": 218,\n// "g": 78,\n// "b": 85,\n// "a": 1\n// }\n// }]\n//}\n')),(0,r.kt)("p",null,"It allows you to capture multiple complex configurations (for example, multiple recommendations or - as in the example above - various colors that can change in the campaign based on the current date)."),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"There is a ",(0,r.kt)("a",{parentName:"p",href:"https://issues.salesforce.com/issue/a028c00000p5gvMAAQ/personalization-incorrect-behaviour-of-array-of-complex-objects-in-templates"},"bug with the removal UI")," for arrays of complex properties."),(0,r.kt)("p",{parentName:"admonition"},"Let's say you have multiple properties configured in an array and want to remove one. If you click the removal button, regardless of which element you did it, the UI will remove the bottom one. However, the correct one was removed in the backend, which you can check in the Payload Preview. Save, reenter the configuration screen or refresh, and you will see the correct configuration.")),(0,r.kt)("h3",{id:"complex-default-values"},"Complex default values"),(0,r.kt)("p",null,"With complex properties, you can provide default values in two ways:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"You can provide default values within the defining class directly on basic properties.")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Define complex property with default values"',title:'"Define',complex:!0,property:!0,with:!0,default:!0,'values"':!0},"export class RecommendationsConfig {\n recommendationsHeader: String = 'Chosen for You';\n recommendationsDisplayed: Number = 6;\n}\n")),(0,r.kt)("p",null,"This approach will work for complex property arrays but not single complex properties."),(0,r.kt)("ol",{start:2},(0,r.kt)("li",{parentName:"ol"},"You can provide a default value in the main class implementing the CampaignTemplateComponent.")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Use complex property with default value"',title:'"Use',complex:!0,property:!0,with:!0,default:!0,'value"':!0},'recsConfig: RecommendationsConfig = {\n "recommendationsHeader": "Chosen for You",\n "recommendationsDisplayed": 6\n};\n')),(0,r.kt)("p",null,"This approach will work for single complex property and - if you provide the default value(s) in the array - also for arrays for complex properties."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Use an array of complex properties with default values"',title:'"Use',an:!0,array:!0,of:!0,complex:!0,properties:!0,with:!0,default:!0,'values"':!0},'recsConfig: RecommendationsConfig = [{\n "recommendationsHeader": "Chosen for You",\n "recommendationsDisplayed": 6\n}, {\n "recommendationsHeader": "Bestsellers",\n "recommendationsDisplayed": 3\n}];\n')),(0,r.kt)("p",null,"As you can see, both approaches work for arrays of complex properties but result in a different outcome. The first approach provides default values to all elements of a complex property array you will create. The second approach prefills the array with the provided default elements. You can use both simultaneously to have a few array elements prefilled and provide default values for all new elements created on top of it."),(0,r.kt)("hr",null),(0,r.kt)("h2",{id:"decorators"},"Decorators"),(0,r.kt)("p",null,"Decorators are a TypeScript feature that can change or extend the behaviour of properties in the MC Personalization's Serverside Code. You have already seen some of them:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"#-select-string"},(0,r.kt)("inlineCode",{parentName:"a"},"@options"))," used to provide picklist values for ",(0,r.kt)("a",{parentName:"li",href:"#-select-string"},"strings"),", ",(0,r.kt)("a",{parentName:"li",href:"#-select-number"},"numbers")," and ",(0,r.kt)("a",{parentName:"li",href:"#complex-picklist"},"complex properties"),"."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"#-rich-text-string"},(0,r.kt)("inlineCode",{parentName:"a"},"@richText"))," that changes string input."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"#number"},(0,r.kt)("inlineCode",{parentName:"a"},"@unit"))," that gives context to a numeric input."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"#-datetime-range"},(0,r.kt)("inlineCode",{parentName:"a"},"@range"))," that modifies date selection into range selection."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"#complex-tabular-view"},(0,r.kt)("inlineCode",{parentName:"a"},"@tabular"))," that changes how a complex object is displayed.")),(0,r.kt)("p",null,"But there is so much more available:"),(0,r.kt)("h3",{id:"title--subtitle"},"@title & @subtitle"),(0,r.kt)("p",null,"When you add a property, its name will be used as a label for the input - in a smart way, with space being added before each uppercase (but not a digit):"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples of default conversion from property name to label"',title:'"Examples',of:!0,default:!0,conversion:!0,from:!0,property:!0,name:!0,to:!0,'label"':!0},"header: string; // Label: Header\nproductDescription: string; // Label: Product Description\nlistElement3: string; // Label: List Element3\n")),(0,r.kt)("p",null,"That's neat, but sometimes you might want to be more descriptive - without changing the actual property passed in the payload. This is where the ",(0,r.kt)("inlineCode",{parentName:"p"},"@title")," decorator comes in. Adding it above a property and passing a string can change the label to anything you want."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{1} title="Examples of default conversion from property name to label"',"{1}":!0,title:'"Examples',of:!0,default:!0,conversion:!0,from:!0,property:!0,name:!0,to:!0,'label"':!0},"@title('Recommendations Box Header')\nheader: string; // Label: Recommendations Box Header\n")),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"You can also pass a space into the ",(0,r.kt)("inlineCode",{parentName:"p"},"@title")," decorator to remove the input label altogether:"),(0,r.kt)("pre",{parentName:"admonition"},(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{1} title="Using space to hide the label"',"{1}":!0,title:'"Using',space:!0,to:!0,hide:!0,the:!0,'label"':!0},"@title(' ')\nheader: string; // No label\n")),(0,r.kt)("p",{parentName:"admonition"},"It can be helpful in some cases, like complex objects with their own label and labels of all properties used to create them, or cases where you want to use ",(0,r.kt)("a",{parentName:"p",href:"#markdown"},(0,r.kt)("inlineCode",{parentName:"a"},"@markdown"))," decorator instead.")),(0,r.kt)("p",null,"On the other hand, adding a ",(0,r.kt)("inlineCode",{parentName:"p"},"@subtitle")," decorator above a property will show the provided text in a smaller font right below the input. It's a great tool to give more context or example data to aid the person configuring the campaign."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{2} title="Example use of @subheader decorator for added context"',"{2}":!0,title:'"Example',use:!0,of:!0,"@subheader":!0,decorator:!0,for:!0,added:!0,'context"':!0},"@title('Recommendations Box Header')\n@subtitle('Use Title Case and stay below 40 characters')\nheader: string;\n")),(0,r.kt)("h3",{id:"markdown"},"@markdown"),(0,r.kt)("p",null,"When ",(0,r.kt)("a",{parentName:"p",href:"#title--subtitle"},(0,r.kt)("inlineCode",{parentName:"a"},"@title")," and ",(0,r.kt)("inlineCode",{parentName:"a"},"@subtitle"))," are not enough for the context you want to provide, you can use the ",(0,r.kt)("inlineCode",{parentName:"p"},"@markdown")," decorator to go wild with text, styling and even links."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{1-6} title="Notice the backticks used to open and close markdown content in this decorator"',"{1-6}":!0,title:'"Notice',the:!0,backticks:!0,used:!0,to:!0,open:!0,and:!0,close:!0,markdown:!0,content:!0,in:!0,this:!0,'decorator"':!0},"@markdown(`\n---\n#### Conditional Configuration\n\n**Use only on campaigns targeted to small audiences**\n`)\n@title('Use Conditional Configuration?')\nisConditionalConfigurationUsed: boolean = false;\n")),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"For ",(0,r.kt)("inlineCode",{parentName:"p"},"@markdown")," to work, you need to add the content without any indentation:"),(0,r.kt)("pre",{parentName:"admonition"},(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{2-5} title="@markdown decorator works only with unindented markdown"',"{2-5}":!0,title:'"@markdown',decorator:!0,works:!0,only:!0,with:!0,unindented:!0,'markdown"':!0}," @markdown(`\n---\n#### Conditional Configuration\n\n**Use only on campaigns targeted to small audiences**\n `)\n @title('Use Conditional Configuration?')\n isConditionalConfigurationUsed: boolean = false;\n"))),(0,r.kt)("h3",{id:"header--headersubtitle"},"@header & @headerSubtitle"),(0,r.kt)("p",null,"There is also a pair of ",(0,r.kt)("inlineCode",{parentName:"p"},"@header")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"@subheader")," decorators that are very similar to ",(0,r.kt)("a",{parentName:"p",href:"#title--subtitle"},(0,r.kt)("inlineCode",{parentName:"a"},"@title")," and ",(0,r.kt)("inlineCode",{parentName:"a"},"@subtitle"))," with one key difference - they are not attaching to a property. That's right, you can use them anywhere to add context to whole sections of your campaign configuration form."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Example use of @subheader decorator for added context"',title:'"Example',use:!0,of:!0,"@subheader":!0,decorator:!0,for:!0,added:!0,'context"':!0},"@header('Recommendations Box Header')\n@headerSubtitle('Use Title Case and stay below 40 characters')\n")),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"@header")," will be in the same font size as the input labels, and ",(0,r.kt)("inlineCode",{parentName:"p"},"@headerSubtitle")," will match ",(0,r.kt)("a",{parentName:"p",href:"#title--subtitle"},(0,r.kt)("inlineCode",{parentName:"a"},"@subtitle"))," style. Oh, and passing a space - ",(0,r.kt)("inlineCode",{parentName:"p"},"@header(' ')")," - will add a bit of whitespace. Perfect for those of us with OCD who can't stand that uneven spacing between inputs."),(0,r.kt)("h3",{id:"hidden--shownif"},"@hidden & @shownIf"),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"@hidden")," & ",(0,r.kt)("inlineCode",{parentName:"p"},"@shownIf")," are some of the most important decorators, as they allow you to control the visibility of the inputs. Big if you want to provide a nice and clean campaign configuration UI that won't overwhelm the marketer."),(0,r.kt)("p",null,"There are two ways to use them."),(0,r.kt)("p",null,"First, with a basic ",(0,r.kt)("inlineCode",{parentName:"p"},"true")," boolean argument, it makes sense only for ",(0,r.kt)("inlineCode",{parentName:"p"},"@hidden"),". It is perfect for data you will calculate in the ",(0,r.kt)("a",{parentName:"p",href:"/docs/interaction-studio/mcp-serverside-code-basics#2-run-block"},"run() block")," or don't want to show to the marketer."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Example use of simple boolean @hidden decorator"',title:'"Example',use:!0,of:!0,simple:!0,boolean:!0,"@hidden":!0,'decorator"':!0},'@hidden(true)\ntemplateVersion: string = "Version 1.1";\n')),(0,r.kt)("p",null,"It gets much more interesting with the second way to use those decorators - with a function that returns a boolean as an argument. With this, you can build conditional logic based on other inputs:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{3}title="Example use of function-based @shownIf decorator"',"{3}title":'"Example',use:!0,of:!0,"function-based":!0,"@shownIf":!0,'decorator"':!0},"bannerType: 'Manual' | 'Promotion' | 'Einstein';\n\n@shownIf(this, (self) => self.bannerType === 'Manual')\nimageURL: string;\n")),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"Technically, in both scenarios, you can use ",(0,r.kt)("inlineCode",{parentName:"p"},"@hidden")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"@shownIf")," interchangeably after appropriately flipping the boolean. However, I find using ",(0,r.kt)("inlineCode",{parentName:"p"},"@hidden")," only with a ",(0,r.kt)("inlineCode",{parentName:"p"},"true")," boolean argument and ",(0,r.kt)("inlineCode",{parentName:"p"},"@shownIf")," with a function argument easier to grasp when reading the code.")),(0,r.kt)("h3",{id:"buttongroup"},"@buttonGroup"),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"@buttonGroup")," is a simple decorator that can change a ",(0,r.kt)("a",{parentName:"p",href:"#-select-string"},(0,r.kt)("inlineCode",{parentName:"a"},"single-select picklist"))," into a set of buttons with one line:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Example use of @buttonGroup decorator"',title:'"Example',use:!0,of:!0,"@buttonGroup":!0,'decorator"':!0},"@buttonGroup(true)\nbannerType: 'Manual' | 'Promotion' | 'Einstein';\n")),(0,r.kt)("p",null,"It's nice if you have a small pool of options with short names."),(0,r.kt)("h3",{id:"optional"},"@optional"),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"@optional")," decorator was an excellent tool for setting required and non-required inputs. Was. It no longer works. Whether you add it to the code or not, nothing will change in the UI or on saving. And I doubt it will start working, as bringing the functionality back would break all templates created with it not working in mind. To sum up, there is no way to enforce filling an input."),(0,r.kt)("hr",null),(0,r.kt)("p",null,"Imports and custom properties coming soon :)"))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmd=self.webpackChunkmd||[]).push([[7531],{4137:(e,t,a)=>{a.d(t,{Zo:()=>u,kt:()=>h});var n=a(7294);function r(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function o(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function i(e){for(var t=1;t=0||(r[a]=e[a]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(r[a]=e[a])}return r}var s=n.createContext({}),p=function(e){var t=n.useContext(s),a=t;return e&&(a="function"==typeof e?e(t):i(i({},t),e)),a},u=function(e){var t=p(e.components);return n.createElement(s.Provider,{value:t},e.children)},d="mdxType",c={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var a=e.components,r=e.mdxType,o=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),d=p(a),m=r,h=d["".concat(s,".").concat(m)]||d[m]||c[m]||o;return a?n.createElement(h,i(i({ref:t},u),{},{components:a})):n.createElement(h,i({ref:t},u))}));function h(e,t){var a=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=a.length,i=new Array(o);i[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[d]="string"==typeof e?e:r,i[1]=l;for(var p=2;p{a.d(t,{m:()=>o});var n=a(7294);const r="leadText_qzwo",o=e=>{let{content:t}=e;return n.createElement(n.Fragment,null,n.createElement("p",{id:r},t))}},5196:(e,t,a)=>{a.r(t),a.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>m,frontMatter:()=>i,metadata:()=>s,toc:()=>u});var n=a(7462),r=(a(7294),a(4137)),o=a(1872);const i={id:"mcp-serverside-code-properties",title:"MCP Serverside Code Properties",sidebar_label:"Serverside Code Properties",description:"Build your marketers' dream campaign configuration UI in Marketing Cloud Personalization (Interaction Studio). All the input magic in one place.",image:"img/og/og-image-mcp-serverside-code-properties.png",tags:["Marketing Cloud","Marketing Cloud Personalization","Interaction Studio","Personalisation","TypeScript"]},l=void 0,s={unversionedId:"interaction-studio/mcp-serverside-code-properties",id:"interaction-studio/mcp-serverside-code-properties",title:"MCP Serverside Code Properties",description:"Build your marketers' dream campaign configuration UI in Marketing Cloud Personalization (Interaction Studio). All the input magic in one place.",source:"@site/docs/interaction-studio/mcp-serverside-code-properties.mdx",sourceDirName:"interaction-studio",slug:"/interaction-studio/mcp-serverside-code-properties",permalink:"/docs/interaction-studio/mcp-serverside-code-properties",draft:!1,editUrl:"https://github.com/MateuszDabrowski/mateuszdabrowski.pl/edit/master/docs/interaction-studio/mcp-serverside-code-properties.mdx",tags:[{label:"Marketing Cloud",permalink:"/docs/tags/marketing-cloud"},{label:"Marketing Cloud Personalization",permalink:"/docs/tags/marketing-cloud-personalization"},{label:"Interaction Studio",permalink:"/docs/tags/interaction-studio"},{label:"Personalisation",permalink:"/docs/tags/personalisation"},{label:"TypeScript",permalink:"/docs/tags/type-script"}],version:"current",lastUpdatedBy:"Mateusz D\u0105browski",lastUpdatedAt:1697495003,formattedLastUpdatedAt:"Oct 16, 2023",frontMatter:{id:"mcp-serverside-code-properties",title:"MCP Serverside Code Properties",sidebar_label:"Serverside Code Properties",description:"Build your marketers' dream campaign configuration UI in Marketing Cloud Personalization (Interaction Studio). All the input magic in one place.",image:"img/og/og-image-mcp-serverside-code-properties.png",tags:["Marketing Cloud","Marketing Cloud Personalization","Interaction Studio","Personalisation","TypeScript"]},sidebar:"docs",previous:{title:"Serverside Code Basics",permalink:"/docs/interaction-studio/mcp-serverside-code-basics"}},p={},u=[{value:"Basic properties",id:"basic-properties",level:2},{value:"Boolean",id:"boolean",level:3},{value:"String",id:"string",level:3},{value:"\u203a Array String",id:"-array-string",level:4},{value:"\u203a Default String",id:"-default-string",level:4},{value:"\u203a Select String",id:"-select-string",level:4},{value:"\u203a Rich Text String",id:"-rich-text-string",level:4},{value:"Number",id:"number",level:3},{value:"\u203a Array Number",id:"-array-number",level:4},{value:"\u203a Default Number",id:"-default-number",level:4},{value:"\u203a Select Number",id:"-select-number",level:4},{value:"Color",id:"color",level:3},{value:"\u203a Default Color",id:"-default-color",level:4},{value:"Datetime",id:"datetime",level:3},{value:"\u203a Datetime Range",id:"-datetime-range",level:4},{value:"Readonly property",id:"readonly-property",level:2},{value:"Complex property",id:"complex-property",level:2},{value:"Complex default values",id:"complex-default-values",level:3},{value:"Decorators",id:"decorators",level:2},{value:"@title & @subtitle",id:"title--subtitle",level:3},{value:"@markdown",id:"markdown",level:3},{value:"@header & @headerSubtitle",id:"header--headersubtitle",level:3},{value:"@hidden & @shownIf",id:"hidden--shownif",level:3},{value:"@buttonGroup",id:"buttongroup",level:3},{value:"@optional",id:"optional",level:3}],d={toc:u},c="wrapper";function m(e){let{components:t,...a}=e;return(0,r.kt)(c,(0,n.Z)({},d,a,{components:t,mdxType:"MDXLayout"}),(0,r.kt)(o.m,{content:"Build your marketers' dream campaign configuration UI in Marketing Cloud Personalization (Interaction Studio). All the input magic in one place.",mdxType:"LeadText"}),(0,r.kt)("p",null,"Once you learn the ",(0,r.kt)("a",{parentName:"p",href:"/docs/interaction-studio/mcp-serverside-code-basics"},"very basics of Serverside Code")," in Marketing Cloud Personalization (Interaction Studio), it is time to start writing code. The easiest way is, to begin with creating the campaign creation UI for the marketer - defining all the inputs they will need to fill in to drive the experience. It will be our focus in this article."),(0,r.kt)("p",null,"MCP Serverside Code offers five basic property data types: ",(0,r.kt)("a",{parentName:"p",href:"#boolean"},"Boolean"),", ",(0,r.kt)("a",{parentName:"p",href:"#string"},"String"),", ",(0,r.kt)("a",{parentName:"p",href:"#number"},"Number"),", ",(0,r.kt)("a",{parentName:"p",href:"#color"},"Color")," and ",(0,r.kt)("a",{parentName:"p",href:"#datime"},"DateTime"),". Those, their combinations into ",(0,r.kt)("a",{parentName:"p",href:"#complex-property"},"complex properties")," and modifications through ",(0,r.kt)("a",{parentName:"p",href:"#decorators"},"decorators")," enable you to build nearly anything you need for your campaign configuration screen."),(0,r.kt)("p",null,"Let's dive into how you can make the most of those features."),(0,r.kt)("h2",{id:"basic-properties"},"Basic properties"),(0,r.kt)("p",null,"Basic properties are the base building blocks of your campaign configuration. They are all you need to make a perfect campaign template, and mastering them will be essential to doing more complex UIs with ",(0,r.kt)("a",{parentName:"p",href:"#complex-property"},"complex properties")," and ",(0,r.kt)("a",{parentName:"p",href:"#decorators"},"decorators"),"."),(0,r.kt)("h3",{id:"boolean"},"Boolean"),(0,r.kt)("p",null,"The boolean property lets you capture a ",(0,r.kt)("inlineCode",{parentName:"p"},"true")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"false")," flag by creating a checkbox input:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Create simple checkbox"',title:'"Create',simple:!0,'checkbox"':!0},"isTextLeftToRight: boolean;\n// Input: Editable unchecked checkbox\n// Output: true or false\n")),(0,r.kt)("p",null,"The boolean property is unchecked by default (returns ",(0,r.kt)("inlineCode",{parentName:"p"},"false"),"), but you can change it by passing a ",(0,r.kt)("inlineCode",{parentName:"p"},"true")," value in the code:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Checkbox checked by default"',title:'"Checkbox',checked:!0,by:!0,'default"':!0},"isTextLeftToRight: boolean = true;\n// Input: Editable checked checkbox\n// Output: true or false\n")),(0,r.kt)("p",null,"Boolean fields are crucial for template development as they are perfect for building logic, for example, with the ",(0,r.kt)("a",{parentName:"p",href:"#hidden--shownif"},(0,r.kt)("inlineCode",{parentName:"a"},"@shownIf")," decorator"),"."),(0,r.kt)("h3",{id:"string"},"String"),(0,r.kt)("p",null,"String property lets you capture a text input:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Create empty text input field"',title:'"Create',empty:!0,text:!0,input:!0,'field"':!0},'content: string;\n// Input: Empty text input\n// Output: "Provided text"\n')),(0,r.kt)("p",null,"However, there is much more to string property than just that."),(0,r.kt)("h4",{id:"-array-string"},"\u203a Array String"),(0,r.kt)("p",null,"You may want to capture more than one string. Easy, you can do it with two characters:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Capture multiple strings"',title:'"Capture',multiple:!0,'strings"':!0},'hashtags: string[];\n// Input: Empty text input with an option for converting entered text into one of the values\n// Output: ["Array", "of", "Strings"]\n')),(0,r.kt)("p",null,"By adding ",(0,r.kt)("inlineCode",{parentName:"p"},"[]")," after the type definition, we convert the field to an array of strings. During configuration, you can provide multiple values that, in the payload, will be returned as an array. It doesn't accept duplicate values."),(0,r.kt)("h4",{id:"-default-string"},"\u203a Default String"),(0,r.kt)("p",null,"To simplify the work for your marketers or to provide recommended examples of expected data, you can easily add a default value to your string by delivering it as a string after the equal sign:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Add default content"',title:'"Add',default:!0,'content"':!0},"content: string = 'This is default content';\n// Input: Editable prefilled text input\n// Output: \"Provided text\"\n")),(0,r.kt)("p",null,"It can be freely edited during configuration but will be passed as-is to the payload if no changes are made."),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"You can provide default values also for string arrays:"),(0,r.kt)("pre",{parentName:"admonition"},(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Default values for array string"',title:'"Default',values:!0,for:!0,array:!0,'string"':!0},'hashtags: string[] = [\'interaction-studio\', \'marketing-cloud-personalization\'];\n// Input: Editable prefilled text input with two deletable values and space to write additional ones\n// Output: ["Array", "of", "Strings"]\n'))),(0,r.kt)("h4",{id:"-select-string"},"\u203a Select String"),(0,r.kt)("p",null,"You can go one step further and lock the string property to only a set of preconfigured values using the ",(0,r.kt)("inlineCode",{parentName:"p"},"@options")," decorator. It is excellent when you need a particular value, for example, for a programmatic logic in other parts of your template."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Limit possible values with select"',title:'"Limit',possible:!0,values:!0,with:!0,'select"':!0},"@options(['brandA', 'brandB', 'brandC'])\nbrand: string;\n// Input: Empty text input showing dropdown with configured options on click\n// Output: \"Selected text\"\n")),(0,r.kt)("p",null,"With such code, the campaign configuration will display a picklist with the hardcoded values. It will output a string to the campaign payload."),(0,r.kt)("p",null,"You can also provide ",(0,r.kt)("a",{parentName:"p",href:"#-default-string"},"default value")," for your select field:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Provide default select value"',title:'"Provide',default:!0,select:!0,'value"':!0},"@options(['brandA', 'brandB', 'brandC'])\nbrand: string = 'brandA';\n// Input: Editable prefilled text input showing dropdown with configured options on click\n// Output: \"Selected text\"\n")),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"You can also create a select field without the decorator using literals:"),(0,r.kt)("pre",{parentName:"admonition"},(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Literal select field"',title:'"Literal',select:!0,'field"':!0},"brand: 'brandA' | 'brandB' | 'brandC';\n// Input: Empty text input showing dropdown with configured options on click\n// Output: \"Selected text\"\n"))),(0,r.kt)("p",null,"Just as you can capture an array of text inputs, you can do the same for select strings and create a multi-picklist. However, while the preconfigured options will be visible and accessible to pick, new options - outside of what you hardcoded - ",(0,r.kt)("strong",{parentName:"p"},"can")," be freely added."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Multi-picklist"',title:'"Multi-picklist"'},"@options(['brandA', 'brandB', 'brandC'])\nbrands: string[];\n// Input: Empty text field with picklist and option to create new values. Allows for multiple additions.\n// Output: [\"Array\", \"of\", \"Strings\"]\n")),(0,r.kt)("p",null,"You can also provide ",(0,r.kt)("a",{parentName:"p",href:"#-default-string"},"default value")," for your multi-select field:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Provide default multi-select values"',title:'"Provide',default:!0,"multi-select":!0,'values"':!0},"@options(['brandA', 'brandB', 'brandC'])\nbrands: string[] = ['brandA', 'brandB'];\n// Input: Editable prefilled text field with picklist and option to create new values. Allows for multiple additions.\n// Output: [\"Array\", \"of\", \"Strings\"]\n")),(0,r.kt)("p",null,"It will output an array of strings to the campaign payload."),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"You can also create a multi-select field without the decorator using literals:"),(0,r.kt)("pre",{parentName:"admonition"},(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Literal multi-select field"',title:'"Literal',"multi-select":!0,'field"':!0},"brand: ('brandA' | 'brandB' | 'brandC')[];\n// Input: Empty text field with picklist and option to create new values. Allows for multiple additions.\n// Output: [\"Array\", \"of\", \"Strings\"]\n"))),(0,r.kt)("h4",{id:"-rich-text-string"},"\u203a Rich Text String"),(0,r.kt)("p",null,"You can easily convert this simple text input into a rich text field by using a ",(0,r.kt)("inlineCode",{parentName:"p"},"@richText")," decorator:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Capture styling with a single decorator"',title:'"Capture',styling:!0,with:!0,a:!0,single:!0,'decorator"':!0},"@richText(true)\ncontent: string;\n// Input: Text input that adapts to the content size and provides bold, italic, underscore and link WYSIWYG options.\n// Output: \"String with optionalHTML
Includes multiline\"\n")),(0,r.kt)("p",null,"This decorator will change the input field to a multiline box with bold, italic, underscore and link features. The payload will return those styles in the form of HTML that can then be used directly, for example, in the Handlebars tab of Web Campaign."),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"While the rich text options are minimal, you can write some other HTML (for example, ",(0,r.kt)("inlineCode",{parentName:"p"},"")," with style attribute) directly in the box, which will be passed to the output correctly. It will even display as a formatted text after you revisit the configuration.")),(0,r.kt)("h3",{id:"number"},"Number"),(0,r.kt)("p",null,"Number property lets you capture integer (",(0,r.kt)("inlineCode",{parentName:"p"},"3"),") and decimal (",(0,r.kt)("inlineCode",{parentName:"p"},"3.14"),") values:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Create empty numeric input"',title:'"Create',empty:!0,numeric:!0,'input"':!0},"recommendationsCount: number;\n// Input: Empty text (sic!) input\n// Output: 3.14\n")),(0,r.kt)("p",null,"Keep in mind that non-numeric values might break the campaign without any error visible in the configuration screen. You can either display a warning using ",(0,r.kt)("a",{parentName:"p",href:"#hidden--shownif"},(0,r.kt)("inlineCode",{parentName:"a"},"@shownIf")," decorator")," and a ",(0,r.kt)("a",{parentName:"p",href:"#string"},"string field")," or create sanitization logic in the run block."),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"You can use ",(0,r.kt)("inlineCode",{parentName:"p"},"@unit")," ",(0,r.kt)("a",{parentName:"p",href:"#decorators"},"decorator")," to provide a unit description next to the input. It has no impact on the outputted value but gives marketer information about the expected value:"),(0,r.kt)("pre",{parentName:"admonition"},(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{1} title="Unit decorator on number"',"{1}":!0,title:'"Unit',decorator:!0,on:!0,'number"':!0},"@unit('ms')\ndelayBeforeDisplay: number;\n"))),(0,r.kt)("h4",{id:"-array-number"},"\u203a Array Number"),(0,r.kt)("p",null,"Just as with strings, you can capture multiple numeric values by adding ",(0,r.kt)("inlineCode",{parentName:"p"},"[]"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Capture multiple numbers"',title:'"Capture',multiple:!0,'numbers"':!0},"productIds: number[];\n// Input: A plus icon that adds new text inputs with delete buttons\n// Output: [3.14, 3, 5]\n")),(0,r.kt)("p",null,"It will output an array of numbers to the campaign payload."),(0,r.kt)("h4",{id:"-default-number"},"\u203a Default Number"),(0,r.kt)("p",null,"Again, just as with strings, you can provide an editable default value:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Default numeric value"',title:'"Default',numeric:!0,'value"':!0},"recommendationsCount: number = 6;\n// Input: Editable prefilled text input\n// Output: 6\n")),(0,r.kt)("p",null,"The same can be done for Array Numbers:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Default numeric array value"',title:'"Default',numeric:!0,array:!0,'value"':!0},"productIds: number[] = [123, 456, 789];\n// Input: Editable and deletable prefilled text inputs and a plus icon that can add more\n// Output: [123, 456, 32]\n\n")),(0,r.kt)("h4",{id:"-select-number"},"\u203a Select Number"),(0,r.kt)("p",null,"As with ",(0,r.kt)("a",{parentName:"p",href:"#-select-string"},"strings"),", you can create a numeric picklist:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Numeric picklist"',title:'"Numeric','picklist"':!0},"recommendationsCount: 1 | 3 | 6 | 9;\n// Input: Empty text input showing dropdown with configured options on click\n// Output: 1\n")),(0,r.kt)("p",null,"You can also pass a default value:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Numeric picklist with default value"',title:'"Numeric',picklist:!0,with:!0,default:!0,'value"':!0},"recommendationsCount: 1 | 3 | 6 | 9 = 6;\n// Input: Editable prefilled text input showing dropdown with configured options on click\n// Output: 6\n")),(0,r.kt)("p",null,"You can also create a numeric multi-picklist:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Numeric multi-picklist"',title:'"Numeric','multi-picklist"':!0},"orderOfSections: (1 | 2 | 3 | 4 | 5)[];\n// Input: Empty text field with picklist. Allows for multiple additions.\n// Output: [1, 3, 4, 2, 5]\n")),(0,r.kt)("p",null,"Different from ",(0,r.kt)("a",{parentName:"p",href:"#-select-string"},"strings multi-select"),", with a numeric multi-picklist, the marketer won't be able to add new options outside of what you preconfigured. Yay!"),(0,r.kt)("p",null,"However, there are three caveats to keep in mind:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"You cannot have ",(0,r.kt)("inlineCode",{parentName:"li"},"0")," as one of the options. The picklist will crash."),(0,r.kt)("li",{parentName:"ol"},"You cannot use ",(0,r.kt)("inlineCode",{parentName:"li"},"@options")," to create the picklist. It will be ignored."),(0,r.kt)("li",{parentName:"ol"},"The single-select picklist will always cut the visibility of the last digit in the longest option after selecting it.")),(0,r.kt)("h3",{id:"color"},"Color"),(0,r.kt)("p",null,"Color property lets you create a color picker with a single line of code:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Create color picker"',title:'"Create',color:!0,'picker"':!0},'backgroundColor: Color;\n// Input: An input pseudo-prefilled with #FFFFFF and a color box that, on click, opens the color selection interface\n// Output: {\n// "hex": "#da4e55",\n// "r": 218,\n// "g": 78,\n// "b": 85,\n// "a": 1\n//}\n')),(0,r.kt)("p",null,"You will be able to select the color by dragging the selector over a colour palette or by providing hex/rgba/hsl values. The property will output to payload a ",(0,r.kt)("inlineCode",{parentName:"p"},"color")," object with both hex and rgba values."),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"While the property will look like it is prefilled with white color, it will output 'null' until you pick a color in the interface. If you want the white to be a working default option, you must configure it explicitly.")),(0,r.kt)("h4",{id:"-default-color"},"\u203a Default Color"),(0,r.kt)("p",null,"You can provide a default color, but it will be more complex. You cannot just give a single hex value; you need to replicate the whole output object:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title"Default color value"','title"Default':!0,color:!0,'value"':!0},'backgroundColor: Color = {\n "hex": "#da4e55",\n "r": 218,\n "g": 78,\n "b": 85,\n "a": 1\n};\n// Input: An input truly prefilled with #FFFFFF and a color box that, on click, opens the color selection interface\n// Output: {\n// "hex": "#da4e55",\n// "r": 218,\n// "g": 78,\n// "b": 85,\n// "a": 1\n//}\n')),(0,r.kt)("h3",{id:"datetime"},"Datetime"),(0,r.kt)("p",null,"Datetime property provides a clean-looking calendar widget that lets the marketer easily pick up a date."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Create datetime picker"',title:'"Create',datetime:!0,'picker"':!0},'promotionDate: DateTime;\n// Input: Empty input with calendar icon that, on click, opens full calendar for date selection\n// Output: {\n// "dateTime": [\n// "2023-10-01T16:00:00.000Z"\n// ]\n//}\n')),(0,r.kt)("p",null,"There are two payload-related things to consider:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"The DateTime property always outputs an object with a single ",(0,r.kt)("inlineCode",{parentName:"li"},"dateTime")," property assigned to an array of strings."),(0,r.kt)("li",{parentName:"ol"},"The date-times are returned as ISO 8601 strings (",(0,r.kt)("inlineCode",{parentName:"li"},"2023-10-01T16:00:00.000Z"),").")),(0,r.kt)("h4",{id:"-datetime-range"},"\u203a Datetime Range"),(0,r.kt)("p",null,"While you cannot create a multi-select datetime field, you can use the ",(0,r.kt)("inlineCode",{parentName:"p"},"@range")," decorator to select two dates within a nice UI."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Create range datetime picker"',title:'"Create',range:!0,datetime:!0,'picker"':!0},'@range(true)\npromotionDate: DateTime;\n// Input: Empty input with calendar icon that on click opens full calendar for two dates selection with range indicator\n// Output: {\n// "dateTime": [\n// "2023-10-01T16:00:00.000Z",\n// "2023-10-03T16:00:00.000Z"\n// ]\n//}\n')),(0,r.kt)("p",null,"The output will return both selected dates in the ",(0,r.kt)("inlineCode",{parentName:"p"},"dateTime")," array of strings."),(0,r.kt)("hr",null),(0,r.kt)("h2",{id:"readonly-property"},"Readonly property"),(0,r.kt)("p",null,"Apart from ",(0,r.kt)("a",{parentName:"p",href:"#string"},"basic string property")," there is also a very similar readonly string property:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Static readonly property"',title:'"Static',readonly:!0,'property"':!0},"readonly templateVersion = \"Version 1.1\"\n// Input: No input, just a readonly text written in the form\n// Output: 'Version 1.1'\n")),(0,r.kt)("p",null,"As you can see, the key difference is the ",(0,r.kt)("inlineCode",{parentName:"p"},"readonly")," prefix before defining the property. It also uses the ",(0,r.kt)("a",{parentName:"p",href:"#-default-string"},"default string")," approach to assign the value."),(0,r.kt)("p",null,"However, readonly property has one superpower. You can assign an arrow function to it (as long as it will return a string) and use values from other properties to transform them:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{2,5-13} title="Dynamic readonly property"',"{2,5-13}":!0,title:'"Dynamic',readonly:!0,'property"':!0},"header: string = 'DEFault heaDER';\nreadonly upperCaseHeader = () => this?.header.toUpperCase || '';\n// Input: No input, just a readonly text updating real-time based on header input\n// Output: 'DEFAULT HEADER'\nreadonly titleCaseHeader = () => {\n return this?.header\n .toLowerCase()\n .split(' ')\n .filter(word => word !== '')\n .map(word => word.replace(word[0], word[0].toUpperCase()))\n .join(' ')\n || '';\n}\n// Input: No input, just a readonly text updating real-time based on header input\n// Output: 'Default Header'\n")),(0,r.kt)("p",null,"With the dynamic readonly property, you can transform string inputs, concatenate multiple inputs into one field or even create a logic based on non-string inputs (as long as the output is a string)."),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"You can do the same things later in the ",(0,r.kt)("a",{parentName:"p",href:"/docs/interaction-studio/mcp-serverside-code-basics#2-run-block"},"run() block"),". It will be even more powerful there, as you can use the context object. However, you must manually add those calculated values to the ",(0,r.kt)("a",{parentName:"p",href:"/docs/interaction-studio/mcp-serverside-code-basics#3-return-statement"},"return statement")," to see it in the payload."),(0,r.kt)("p",{parentName:"admonition"},"For simple use cases, the readonly property will be more straightforward and provide a nifty real-time preview of the calculated value for the marketer.")),(0,r.kt)("hr",null),(0,r.kt)("h2",{id:"complex-property"},"Complex property"),(0,r.kt)("p",null,"The input configuration fun starts when you combine ",(0,r.kt)("a",{parentName:"p",href:"#basic-properties"},"basic properties")," into complex ones. The method is straightforward. Outside the main class that implements CampaignTemplateComponent, create a new class export that contains all the basic inputs you need:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Define complex property"',title:'"Define',complex:!0,'property"':!0},"export class TimeframedColorPicker {\n @range(true)\n timeframe: DateTime;\n\n color: Color;\n}\n")),(0,r.kt)("p",null,"With complex property defined, you can leverage it in the main class the same way as you do with basic properties:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Use complex property"',title:'"Use',complex:!0,'property"':!0},'timeframedColor: TimeframedColorPicker;\n// Input: Set of basic properties - datetime range and color pickers, in this case\n// Output: {\n// timeframedColor: {\n// timeframe: "dateTime": [\n// "2023-10-01T16:00:00.000Z",\n// "2023-10-03T16:00:00.000Z"\n// ],\n// color: {\n// "hex": "#da4e55",\n// "r": 218,\n// "g": 78,\n// "b": 85,\n// "a": 1\n// }\n// }\n//}\n')),(0,r.kt)("p",null,"What are the benefits of this approach?"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"You can define your complex property class once and then use it multiple times in your form."),(0,r.kt)("li",{parentName:"ol"},"You can control the structure of the Serverside payload.")),(0,r.kt)("p",null,"A complex property creates a new nested object assigned to an input property. Let's look at the difference:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Payload using basic properties"',title:'"Payload',using:!0,basic:!0,'properties"':!0},'@range(true)\ntimeframe: Date;\n\ncolor: Color;\n// Input: Two basic properties - datetime range and color pickers, in this case\n// Output: {\n// timeframe: "dateTime": [\n// "2023-10-01T16:00:00.000Z",\n// "2023-10-03T16:00:00.000Z"\n// ],\n// color: {\n// "hex": "#da4e55",\n// "r": 218,\n// "g": 78,\n// "b": 85,\n// "a": 1\n// }\n//}\n')),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{4,16} title="Payload using complex property"',"{4,16}":!0,title:'"Payload',using:!0,complex:!0,'property"':!0},'timeframedColor: TimeframedColorPicker;\n// Input: Set of basic properties - datetime range and color pickers in this case\n// Output: {\n// timeframedColor: {\n// timeframe: "dateTime": [\n// "2023-10-01T16:00:00.000Z",\n// "2023-10-03T16:00:00.000Z"\n// ],\n// color: {\n// "hex": "#da4e55",\n// "r": 218,\n// "g": 78,\n// "b": 85,\n// "a": 1\n// }\n// }\n//}\n')),(0,r.kt)("p",null,"As you can see, data from basic inputs is assigned as properties to the complex property. This can help with payload readability and might be crucial when adapting your payload to specific requirements (for example, schema expected by a React website or 3rd party system)."),(0,r.kt)("ol",{start:3},(0,r.kt)("li",{parentName:"ol"},"You can nest complex properties")),(0,r.kt)("p",null,"Just as you can add a complex property to a field, you can also create a complex property using complex properties. While it can again give you all the benefits mentioned here, be careful not to go overboard. Deep nesting is more challenging to understand and use. There is no perfect rule, but check out ",(0,r.kt)("a",{parentName:"p",href:"../../zen-of-sfmc/#simple-is-better-than-complex"},"Simple/Complex recommendations in Zen of SFMC"),"."),(0,r.kt)("ol",{start:4},(0,r.kt)("li",{parentName:"ol"},"You can leverage it to build an Array of complex properties!")),(0,r.kt)("p",null,"This is the real game changer - out of the basic properties, only ",(0,r.kt)("a",{parentName:"p",href:"#-array-string"},"Strings")," and ",(0,r.kt)("a",{parentName:"p",href:"#-array-string"},"Numbers")," can be used as arrays. But using a complex one, you can also leverage Boolean, Color and Datetime. The approach is the same as previously - just add ",(0,r.kt)("inlineCode",{parentName:"p"},"[]"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{4,16} title="Array of complex properties"',"{4,16}":!0,title:'"Array',of:!0,complex:!0,'properties"':!0},'timeframedColor: TimeframedColorPicker[];\n// Input: A plus icon that adds new sets of basic properties with delete buttons\n// Output: {\n// timeframedColor: [{\n// timeframe: "dateTime": [\n// "2023-10-01T16:00:00.000Z",\n// "2023-10-03T16:00:00.000Z"\n// ],\n// color: {\n// "hex": "#da4e55",\n// "r": 218,\n// "g": 78,\n// "b": 85,\n// "a": 1\n// }\n// }]\n//}\n')),(0,r.kt)("p",null,"It allows you to capture multiple complex configurations (for example, multiple recommendations or - as in the example above - various colors that can change in the campaign based on the current date)."),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"There is a ",(0,r.kt)("a",{parentName:"p",href:"https://issues.salesforce.com/issue/a028c00000p5gvMAAQ/personalization-incorrect-behaviour-of-array-of-complex-objects-in-templates"},"bug with the removal UI")," for arrays of complex properties."),(0,r.kt)("p",{parentName:"admonition"},"Let's say you have multiple properties configured in an array and want to remove one. If you click the removal button, regardless of which element you did it, the UI will remove the bottom one. However, the correct one was removed in the backend, which you can check in the Payload Preview. Save, reenter the configuration screen or refresh, and you will see the correct configuration.")),(0,r.kt)("h3",{id:"complex-default-values"},"Complex default values"),(0,r.kt)("p",null,"With complex properties, you can provide default values in two ways:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"You can provide default values within the defining class directly on basic properties.")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Define complex property with default values"',title:'"Define',complex:!0,property:!0,with:!0,default:!0,'values"':!0},"export class RecommendationsConfig {\n recommendationsHeader: String = 'Chosen for You';\n recommendationsDisplayed: Number = 6;\n}\n")),(0,r.kt)("p",null,"This approach will work for complex property arrays but not single complex properties."),(0,r.kt)("ol",{start:2},(0,r.kt)("li",{parentName:"ol"},"You can provide a default value in the main class implementing the CampaignTemplateComponent.")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Use complex property with default value"',title:'"Use',complex:!0,property:!0,with:!0,default:!0,'value"':!0},'recsConfig: RecommendationsConfig = {\n "recommendationsHeader": "Chosen for You",\n "recommendationsDisplayed": 6\n};\n')),(0,r.kt)("p",null,"This approach will work for single complex property and - if you provide the default value(s) in the array - also for arrays for complex properties."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Use an array of complex properties with default values"',title:'"Use',an:!0,array:!0,of:!0,complex:!0,properties:!0,with:!0,default:!0,'values"':!0},'recsConfig: RecommendationsConfig = [{\n "recommendationsHeader": "Chosen for You",\n "recommendationsDisplayed": 6\n}, {\n "recommendationsHeader": "Bestsellers",\n "recommendationsDisplayed": 3\n}];\n')),(0,r.kt)("p",null,"As you can see, both approaches work for arrays of complex properties but result in a different outcome. The first approach provides default values to all elements of a complex property array you will create. The second approach prefills the array with the provided default elements. You can use both simultaneously to have a few array elements prefilled and provide default values for all new elements created on top of it."),(0,r.kt)("hr",null),(0,r.kt)("h2",{id:"decorators"},"Decorators"),(0,r.kt)("p",null,"Decorators are a TypeScript feature that can change or extend the behaviour of properties in the MC Personalization's Serverside Code. You have already seen some of them:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"#-select-string"},(0,r.kt)("inlineCode",{parentName:"a"},"@options"))," used to provide picklist values for ",(0,r.kt)("a",{parentName:"li",href:"#-select-string"},"strings"),", ",(0,r.kt)("a",{parentName:"li",href:"#-select-number"},"numbers")," and ",(0,r.kt)("a",{parentName:"li",href:"#complex-picklist"},"complex properties"),"."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"#-rich-text-string"},(0,r.kt)("inlineCode",{parentName:"a"},"@richText"))," that changes string input."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"#number"},(0,r.kt)("inlineCode",{parentName:"a"},"@unit"))," that gives context to a numeric input."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"#-datetime-range"},(0,r.kt)("inlineCode",{parentName:"a"},"@range"))," that modifies date selection into range selection."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"#complex-tabular-view"},(0,r.kt)("inlineCode",{parentName:"a"},"@tabular"))," that changes how a complex object is displayed.")),(0,r.kt)("p",null,"But there is so much more available:"),(0,r.kt)("h3",{id:"title--subtitle"},"@title & @subtitle"),(0,r.kt)("p",null,"When you add a property, its name will be used as a label for the input - in a smart way, with space being added before each uppercase (but not a digit):"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples of default conversion from property name to label"',title:'"Examples',of:!0,default:!0,conversion:!0,from:!0,property:!0,name:!0,to:!0,'label"':!0},"header: string; // Label: Header\nproductDescription: string; // Label: Product Description\nlistElement3: string; // Label: List Element3\n")),(0,r.kt)("p",null,"That's neat, but sometimes you might want to be more descriptive - without changing the actual property passed in the payload. This is where the ",(0,r.kt)("inlineCode",{parentName:"p"},"@title")," decorator comes in. Adding it above a property and passing a string can change the label to anything you want."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{1} title="Examples of default conversion from property name to label"',"{1}":!0,title:'"Examples',of:!0,default:!0,conversion:!0,from:!0,property:!0,name:!0,to:!0,'label"':!0},"@title('Recommendations Box Header')\nheader: string; // Label: Recommendations Box Header\n")),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"You can also pass a space into the ",(0,r.kt)("inlineCode",{parentName:"p"},"@title")," decorator to remove the input label altogether:"),(0,r.kt)("pre",{parentName:"admonition"},(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{1} title="Using space to hide the label"',"{1}":!0,title:'"Using',space:!0,to:!0,hide:!0,the:!0,'label"':!0},"@title(' ')\nheader: string; // No label\n")),(0,r.kt)("p",{parentName:"admonition"},"It can be helpful in some cases, like complex objects with their own label and labels of all properties used to create them, or cases where you want to use ",(0,r.kt)("a",{parentName:"p",href:"#markdown"},(0,r.kt)("inlineCode",{parentName:"a"},"@markdown"))," decorator instead.")),(0,r.kt)("p",null,"On the other hand, adding a ",(0,r.kt)("inlineCode",{parentName:"p"},"@subtitle")," decorator above a property will show the provided text in a smaller font right below the input. It's a great tool to give more context or example data to aid the person configuring the campaign."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{2} title="Example use of @subheader decorator for added context"',"{2}":!0,title:'"Example',use:!0,of:!0,"@subheader":!0,decorator:!0,for:!0,added:!0,'context"':!0},"@title('Recommendations Box Header')\n@subtitle('Use Title Case and stay below 40 characters')\nheader: string;\n")),(0,r.kt)("h3",{id:"markdown"},"@markdown"),(0,r.kt)("p",null,"When ",(0,r.kt)("a",{parentName:"p",href:"#title--subtitle"},(0,r.kt)("inlineCode",{parentName:"a"},"@title")," and ",(0,r.kt)("inlineCode",{parentName:"a"},"@subtitle"))," are not enough for the context you want to provide, you can use the ",(0,r.kt)("inlineCode",{parentName:"p"},"@markdown")," decorator to go wild with text, styling and even links."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{1-6} title="Notice the backticks used to open and close markdown content in this decorator"',"{1-6}":!0,title:'"Notice',the:!0,backticks:!0,used:!0,to:!0,open:!0,and:!0,close:!0,markdown:!0,content:!0,in:!0,this:!0,'decorator"':!0},"@markdown(`\n---\n#### Conditional Configuration\n\n**Use only on campaigns targeted to small audiences**\n`)\n@title('Use Conditional Configuration?')\nisConditionalConfigurationUsed: boolean = false;\n")),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"For ",(0,r.kt)("inlineCode",{parentName:"p"},"@markdown")," to work, you need to add the content without any indentation:"),(0,r.kt)("pre",{parentName:"admonition"},(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{2-5} title="@markdown decorator works only with unindented markdown"',"{2-5}":!0,title:'"@markdown',decorator:!0,works:!0,only:!0,with:!0,unindented:!0,'markdown"':!0}," @markdown(`\n---\n#### Conditional Configuration\n\n**Use only on campaigns targeted to small audiences**\n `)\n @title('Use Conditional Configuration?')\n isConditionalConfigurationUsed: boolean = false;\n"))),(0,r.kt)("h3",{id:"header--headersubtitle"},"@header & @headerSubtitle"),(0,r.kt)("p",null,"There is also a pair of ",(0,r.kt)("inlineCode",{parentName:"p"},"@header")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"@subheader")," decorators that are very similar to ",(0,r.kt)("a",{parentName:"p",href:"#title--subtitle"},(0,r.kt)("inlineCode",{parentName:"a"},"@title")," and ",(0,r.kt)("inlineCode",{parentName:"a"},"@subtitle"))," with one key difference - they are not attaching to a property. That's right, you can use them anywhere to add context to whole sections of your campaign configuration form."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Example use of @subheader decorator for added context"',title:'"Example',use:!0,of:!0,"@subheader":!0,decorator:!0,for:!0,added:!0,'context"':!0},"@header('Recommendations Box Header')\n@headerSubtitle('Use Title Case and stay below 40 characters')\n")),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"@header")," will be in the same font size as the input labels, and ",(0,r.kt)("inlineCode",{parentName:"p"},"@headerSubtitle")," will match ",(0,r.kt)("a",{parentName:"p",href:"#title--subtitle"},(0,r.kt)("inlineCode",{parentName:"a"},"@subtitle"))," style. Oh, and passing a space - ",(0,r.kt)("inlineCode",{parentName:"p"},"@header(' ')")," - will add a bit of whitespace. Perfect for those of us with OCD who can't stand that uneven spacing between inputs."),(0,r.kt)("h3",{id:"hidden--shownif"},"@hidden & @shownIf"),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"@hidden")," & ",(0,r.kt)("inlineCode",{parentName:"p"},"@shownIf")," are some of the most important decorators, as they allow you to control the visibility of the inputs. Big if you want to provide a nice and clean campaign configuration UI that won't overwhelm the marketer."),(0,r.kt)("p",null,"There are two ways to use them."),(0,r.kt)("p",null,"First, with a basic ",(0,r.kt)("inlineCode",{parentName:"p"},"true")," boolean argument, it makes sense only for ",(0,r.kt)("inlineCode",{parentName:"p"},"@hidden"),". It is perfect for data you will calculate in the ",(0,r.kt)("a",{parentName:"p",href:"/docs/interaction-studio/mcp-serverside-code-basics#2-run-block"},"run() block")," or don't want to show to the marketer."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Example use of simple boolean @hidden decorator"',title:'"Example',use:!0,of:!0,simple:!0,boolean:!0,"@hidden":!0,'decorator"':!0},'@hidden(true)\ntemplateVersion: string = "Version 1.1";\n')),(0,r.kt)("p",null,"It gets much more interesting with the second way to use those decorators - with a function that returns a boolean as an argument. With this, you can build conditional logic based on other inputs:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'{3}title="Example use of function-based @shownIf decorator"',"{3}title":'"Example',use:!0,of:!0,"function-based":!0,"@shownIf":!0,'decorator"':!0},"bannerType: 'Manual' | 'Promotion' | 'Einstein';\n\n@shownIf(this, (self) => self.bannerType === 'Manual')\nimageURL: string;\n")),(0,r.kt)("admonition",{title:"You Should Know",type:"note"},(0,r.kt)("p",{parentName:"admonition"},"Technically, in both scenarios, you can use ",(0,r.kt)("inlineCode",{parentName:"p"},"@hidden")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"@shownIf")," interchangeably after appropriately flipping the boolean. However, I find using ",(0,r.kt)("inlineCode",{parentName:"p"},"@hidden")," only with a ",(0,r.kt)("inlineCode",{parentName:"p"},"true")," boolean argument and ",(0,r.kt)("inlineCode",{parentName:"p"},"@shownIf")," with a function argument easier to grasp when reading the code.")),(0,r.kt)("h3",{id:"buttongroup"},"@buttonGroup"),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"@buttonGroup")," is a simple decorator that can change a ",(0,r.kt)("a",{parentName:"p",href:"#-select-string"},(0,r.kt)("inlineCode",{parentName:"a"},"single-select picklist"))," into a set of buttons with one line:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Example use of @buttonGroup decorator"',title:'"Example',use:!0,of:!0,"@buttonGroup":!0,'decorator"':!0},"@buttonGroup(true)\nbannerType: 'Manual' | 'Promotion' | 'Einstein';\n")),(0,r.kt)("p",null,"It's nice if you have a small pool of options with short names."),(0,r.kt)("h3",{id:"optional"},"@optional"),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"@optional")," decorator was an excellent tool for setting required and non-required inputs. Was. It no longer works. Whether you add it to the code or not, nothing will change in the UI or on saving. And I doubt it will start working, as bringing the functionality back would break all templates created with it not working in mind. To sum up, there is no way to enforce filling an input."),(0,r.kt)("hr",null),(0,r.kt)("p",null,"Imports and custom properties coming soon :)"))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/runtime~main.53e50b68.js b/assets/js/runtime~main.02979f95.js similarity index 99% rename from assets/js/runtime~main.53e50b68.js rename to assets/js/runtime~main.02979f95.js index e88c18ae1..2df6e4f62 100644 --- a/assets/js/runtime~main.53e50b68.js +++ b/assets/js/runtime~main.02979f95.js @@ -1 +1 @@ -(()=>{"use strict";var e,a,f,d,c={},b={};function t(e){var a=b[e];if(void 0!==a)return a.exports;var f=b[e]={id:e,loaded:!1,exports:{}};return c[e].call(f.exports,f,f.exports,t),f.loaded=!0,f.exports}t.m=c,t.c=b,e=[],t.O=(a,f,d,c)=>{if(!f){var b=1/0;for(i=0;i=c)&&Object.keys(t.O).every((e=>t.O[e](f[o])))?f.splice(o--,1):(r=!1,c0&&e[i-1][2]>c;i--)e[i]=e[i-1];e[i]=[f,d,c]},t.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return t.d(a,{a:a}),a},f=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,t.t=function(e,d){if(1&d&&(e=this(e)),8&d)return e;if("object"==typeof e&&e){if(4&d&&e.__esModule)return e;if(16&d&&"function"==typeof e.then)return e}var c=Object.create(null);t.r(c);var b={};a=a||[null,f({}),f([]),f(f)];for(var r=2&d&&e;"object"==typeof r&&!~a.indexOf(r);r=f(r))Object.getOwnPropertyNames(r).forEach((a=>b[a]=()=>e[a]));return b.default=()=>e,t.d(c,b),c},t.d=(e,a)=>{for(var f in a)t.o(a,f)&&!t.o(e,f)&&Object.defineProperty(e,f,{enumerable:!0,get:a[f]})},t.f={},t.e=e=>Promise.all(Object.keys(t.f).reduce(((a,f)=>(t.f[f](e,a),a)),[])),t.u=e=>"assets/js/"+({53:"935f2afb",130:"0ae20921",299:"6ef4e5a9",362:"75f11010",770:"ba7840dc",800:"1328d02f",840:"5ee7ecad",924:"b39e9a29",927:"42b43273",945:"67a57efa",952:"a8b77297",998:"e5fc05e6",1074:"dfa9382d",1092:"7f9f5c99",1099:"407fe916",1247:"d164ef0a",1351:"7eaafb14",1441:"d4ed38e0",1572:"f564aeae",1654:"ecd0154e",1694:"9be7a4ef",1697:"7a40f54e",1837:"ce321ae3",1872:"20e039ae",2050:"1843ef45",2163:"ff6cdf17",2244:"8440116e",2618:"b463a77c",2640:"9944d2ae",2731:"15878b2f",2831:"655aa231",2907:"f881cc1a",3009:"8a538aec",3108:"612d51b5",3157:"772c8612",3207:"22145d1a",3275:"b33fbe49",3447:"cafad187",3466:"4d6aece4",3605:"ff324314",3637:"48b2f688",3664:"369b6fa3",3687:"30084dac",3751:"3720c009",3786:"c8a2287d",3864:"8e6f5683",3877:"05bf1b0a",4027:"fa7b3a47",4121:"55960ee5",4134:"6d83c2a5",4195:"c4f5d8e4",4200:"92cda9bf",4219:"7f3cd917",4331:"2e2f252f",4414:"66b75e7a",4502:"dc8b40ff",4507:"7827587b",4514:"d44a7d50",4650:"a6709703",4715:"8dfb128c",4799:"18f12793",4853:"7046da09",4941:"c792d462",5169:"9a63677c",5244:"240325d0",5295:"6f35f557",5354:"aeba84fb",5406:"9e4ad429",5413:"5ad68b42",5590:"81e44c7d",5605:"0cb3f831",5647:"99db9621",5811:"b84a6020",5831:"3b360413",6050:"f4b177ec",6091:"78bdd589",6167:"2cf43088",6440:"9177d15f",6576:"33ffb019",6583:"c0a0d864",6606:"09792c4b",6639:"c7794043",6795:"0b7df9a2",7008:"6a99b79c",7120:"ecbe7f2c",7207:"d5687ba7",7215:"14add631",7279:"420d9621",7531:"937990e1",7542:"b840d82f",7576:"ecf138d3",7706:"ff2c7cca",7805:"fd9368c3",7873:"ec6dc5c4",7918:"17896441",7920:"1a4e3797",7921:"c64b8421",7983:"cb12d9b9",8421:"e5aa2e7b",8537:"13f9d961",8645:"3365a9e9",8693:"b52e5d8d",8700:"fe4d0c3d",8773:"bb5d57b6",8798:"2b07918c",8819:"edb10261",8833:"c60f9f65",8845:"06bea203",8847:"3b7e54b4",9021:"409b673b",9117:"e2615d27",9134:"509f2ea4",9449:"d2c975d1",9484:"5ee690fc",9514:"1be78505",9517:"139f0dd3",9817:"14eb3368",9855:"0e89a207",9865:"f3b2d393",9887:"45ce19ba",9922:"7e516c75",9924:"df203c0f",9999:"a4dfa1d7"}[e]||e)+"."+{53:"fc80ed25",130:"c9e843a1",299:"cb5cf355",362:"669b6480",770:"666c2377",800:"94855e42",840:"7dfd9771",924:"8736aa8d",927:"37d4a00c",945:"7afccd58",952:"98a212dc",998:"eebfc85c",1074:"6fc496ec",1092:"6a4713de",1099:"9f16b5e1",1247:"5dfdf68d",1351:"3ea7593f",1426:"71674b4b",1441:"4a5c20ff",1572:"7ebdc609",1654:"f2e5d890",1694:"8d8e7884",1697:"60f7c7e3",1765:"bbf8e0e8",1837:"110e20cb",1872:"d0481e11",2050:"001f1829",2153:"ba5319f1",2163:"1c6a804c",2244:"c4f35040",2618:"2afc4922",2640:"7565d3dd",2731:"01d9ed00",2831:"fc8c5aed",2907:"11f5b3c0",3009:"125cb74f",3108:"6e1d1434",3157:"e1066268",3207:"43f6683b",3275:"bec08f3e",3447:"db005bf0",3466:"ad869df5",3605:"34bac101",3637:"f0eeff23",3664:"e8674569",3687:"ce88301b",3751:"465c883a",3786:"feb5464a",3864:"2ea92753",3877:"edf8973f",4027:"945299aa",4121:"16f30936",4134:"0534db3b",4195:"8f51117e",4200:"c1740670",4219:"b05fc5c0",4248:"360e6c79",4331:"5cf41d20",4414:"8467bdee",4502:"885ab7e7",4507:"59bf63a4",4514:"d1e2179b",4650:"84b2269e",4715:"01423c01",4799:"eca4f6de",4853:"3aaf63ac",4941:"4abaf11f",5169:"4f4aa057",5244:"418ae9b0",5295:"0bd177a2",5354:"db0b222d",5406:"17fdd2f9",5413:"ef3c436d",5590:"eef205bf",5605:"0bbf7690",5647:"86977d30",5811:"1c18e841",5831:"da161dd4",6050:"d55e6b4d",6091:"4ea7e967",6167:"e4910f97",6316:"2009feb9",6440:"9dbda4fa",6576:"c602af21",6583:"e50fa5e0",6606:"6060a3e7",6639:"ba82b1ba",6795:"5f8000b0",6945:"a569def7",7008:"df1b5be0",7120:"13620c1b",7207:"6c478ae9",7215:"316e2c03",7279:"a7b1a4cb",7531:"a96e3384",7542:"d2e54469",7576:"93a412ab",7706:"58ca14b1",7724:"4286755f",7805:"cd3f9f7b",7873:"60577ed4",7918:"957336a6",7920:"dcd342f8",7921:"ae233fe9",7983:"db528504",8421:"ef84462c",8537:"b940ac19",8645:"0672b943",8693:"ba7e203c",8700:"3a405712",8773:"0d958107",8798:"e2871f9b",8819:"87a0b6a3",8833:"7c0f6cf1",8845:"01f84522",8847:"d7870ec0",9021:"246861c2",9117:"c2585def",9134:"61b2f7a5",9449:"00633d8b",9484:"579e0ecc",9487:"178ea76e",9514:"6fd0c9a9",9517:"86b16f74",9817:"96621dc4",9855:"ca99072c",9865:"dd9463de",9887:"938d0fd2",9922:"81ba2899",9924:"6fd6f28b",9999:"8d4c3a82"}[e]+".js",t.miniCssF=e=>{},t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),t.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),d={},t.l=(e,a,f,c)=>{if(d[e])d[e].push(a);else{var b,r;if(void 0!==f)for(var o=document.getElementsByTagName("script"),n=0;n{b.onerror=b.onload=null,clearTimeout(l);var c=d[e];if(delete d[e],b.parentNode&&b.parentNode.removeChild(b),c&&c.forEach((e=>e(f))),a)return a(f)},l=setTimeout(u.bind(null,void 0,{type:"timeout",target:b}),12e4);b.onerror=u.bind(null,b.onerror),b.onload=u.bind(null,b.onload),r&&document.head.appendChild(b)}},t.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.p="/",t.gca=function(e){return e={17896441:"7918","935f2afb":"53","0ae20921":"130","6ef4e5a9":"299","75f11010":"362",ba7840dc:"770","1328d02f":"800","5ee7ecad":"840",b39e9a29:"924","42b43273":"927","67a57efa":"945",a8b77297:"952",e5fc05e6:"998",dfa9382d:"1074","7f9f5c99":"1092","407fe916":"1099",d164ef0a:"1247","7eaafb14":"1351",d4ed38e0:"1441",f564aeae:"1572",ecd0154e:"1654","9be7a4ef":"1694","7a40f54e":"1697",ce321ae3:"1837","20e039ae":"1872","1843ef45":"2050",ff6cdf17:"2163","8440116e":"2244",b463a77c:"2618","9944d2ae":"2640","15878b2f":"2731","655aa231":"2831",f881cc1a:"2907","8a538aec":"3009","612d51b5":"3108","772c8612":"3157","22145d1a":"3207",b33fbe49:"3275",cafad187:"3447","4d6aece4":"3466",ff324314:"3605","48b2f688":"3637","369b6fa3":"3664","30084dac":"3687","3720c009":"3751",c8a2287d:"3786","8e6f5683":"3864","05bf1b0a":"3877",fa7b3a47:"4027","55960ee5":"4121","6d83c2a5":"4134",c4f5d8e4:"4195","92cda9bf":"4200","7f3cd917":"4219","2e2f252f":"4331","66b75e7a":"4414",dc8b40ff:"4502","7827587b":"4507",d44a7d50:"4514",a6709703:"4650","8dfb128c":"4715","18f12793":"4799","7046da09":"4853",c792d462:"4941","9a63677c":"5169","240325d0":"5244","6f35f557":"5295",aeba84fb:"5354","9e4ad429":"5406","5ad68b42":"5413","81e44c7d":"5590","0cb3f831":"5605","99db9621":"5647",b84a6020:"5811","3b360413":"5831",f4b177ec:"6050","78bdd589":"6091","2cf43088":"6167","9177d15f":"6440","33ffb019":"6576",c0a0d864:"6583","09792c4b":"6606",c7794043:"6639","0b7df9a2":"6795","6a99b79c":"7008",ecbe7f2c:"7120",d5687ba7:"7207","14add631":"7215","420d9621":"7279","937990e1":"7531",b840d82f:"7542",ecf138d3:"7576",ff2c7cca:"7706",fd9368c3:"7805",ec6dc5c4:"7873","1a4e3797":"7920",c64b8421:"7921",cb12d9b9:"7983",e5aa2e7b:"8421","13f9d961":"8537","3365a9e9":"8645",b52e5d8d:"8693",fe4d0c3d:"8700",bb5d57b6:"8773","2b07918c":"8798",edb10261:"8819",c60f9f65:"8833","06bea203":"8845","3b7e54b4":"8847","409b673b":"9021",e2615d27:"9117","509f2ea4":"9134",d2c975d1:"9449","5ee690fc":"9484","1be78505":"9514","139f0dd3":"9517","14eb3368":"9817","0e89a207":"9855",f3b2d393:"9865","45ce19ba":"9887","7e516c75":"9922",df203c0f:"9924",a4dfa1d7:"9999"}[e]||e,t.p+t.u(e)},(()=>{var e={1303:0,532:0};t.f.j=(a,f)=>{var d=t.o(e,a)?e[a]:void 0;if(0!==d)if(d)f.push(d[2]);else if(/^(1303|532)$/.test(a))e[a]=0;else{var c=new Promise(((f,c)=>d=e[a]=[f,c]));f.push(d[2]=c);var b=t.p+t.u(a),r=new Error;t.l(b,(f=>{if(t.o(e,a)&&(0!==(d=e[a])&&(e[a]=void 0),d)){var c=f&&("load"===f.type?"missing":f.type),b=f&&f.target&&f.target.src;r.message="Loading chunk "+a+" failed.\n("+c+": "+b+")",r.name="ChunkLoadError",r.type=c,r.request=b,d[1](r)}}),"chunk-"+a,a)}},t.O.j=a=>0===e[a];var a=(a,f)=>{var d,c,b=f[0],r=f[1],o=f[2],n=0;if(b.some((a=>0!==e[a]))){for(d in r)t.o(r,d)&&(t.m[d]=r[d]);if(o)var i=o(t)}for(a&&a(f);n{"use strict";var e,a,f,d,c={},b={};function t(e){var a=b[e];if(void 0!==a)return a.exports;var f=b[e]={id:e,loaded:!1,exports:{}};return c[e].call(f.exports,f,f.exports,t),f.loaded=!0,f.exports}t.m=c,t.c=b,e=[],t.O=(a,f,d,c)=>{if(!f){var b=1/0;for(i=0;i=c)&&Object.keys(t.O).every((e=>t.O[e](f[o])))?f.splice(o--,1):(r=!1,c0&&e[i-1][2]>c;i--)e[i]=e[i-1];e[i]=[f,d,c]},t.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return t.d(a,{a:a}),a},f=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,t.t=function(e,d){if(1&d&&(e=this(e)),8&d)return e;if("object"==typeof e&&e){if(4&d&&e.__esModule)return e;if(16&d&&"function"==typeof e.then)return e}var c=Object.create(null);t.r(c);var b={};a=a||[null,f({}),f([]),f(f)];for(var r=2&d&&e;"object"==typeof r&&!~a.indexOf(r);r=f(r))Object.getOwnPropertyNames(r).forEach((a=>b[a]=()=>e[a]));return b.default=()=>e,t.d(c,b),c},t.d=(e,a)=>{for(var f in a)t.o(a,f)&&!t.o(e,f)&&Object.defineProperty(e,f,{enumerable:!0,get:a[f]})},t.f={},t.e=e=>Promise.all(Object.keys(t.f).reduce(((a,f)=>(t.f[f](e,a),a)),[])),t.u=e=>"assets/js/"+({53:"935f2afb",130:"0ae20921",299:"6ef4e5a9",362:"75f11010",770:"ba7840dc",800:"1328d02f",840:"5ee7ecad",924:"b39e9a29",927:"42b43273",945:"67a57efa",952:"a8b77297",998:"e5fc05e6",1074:"dfa9382d",1092:"7f9f5c99",1099:"407fe916",1247:"d164ef0a",1351:"7eaafb14",1441:"d4ed38e0",1572:"f564aeae",1654:"ecd0154e",1694:"9be7a4ef",1697:"7a40f54e",1837:"ce321ae3",1872:"20e039ae",2050:"1843ef45",2163:"ff6cdf17",2244:"8440116e",2618:"b463a77c",2640:"9944d2ae",2731:"15878b2f",2831:"655aa231",2907:"f881cc1a",3009:"8a538aec",3108:"612d51b5",3157:"772c8612",3207:"22145d1a",3275:"b33fbe49",3447:"cafad187",3466:"4d6aece4",3605:"ff324314",3637:"48b2f688",3664:"369b6fa3",3687:"30084dac",3751:"3720c009",3786:"c8a2287d",3864:"8e6f5683",3877:"05bf1b0a",4027:"fa7b3a47",4121:"55960ee5",4134:"6d83c2a5",4195:"c4f5d8e4",4200:"92cda9bf",4219:"7f3cd917",4331:"2e2f252f",4414:"66b75e7a",4502:"dc8b40ff",4507:"7827587b",4514:"d44a7d50",4650:"a6709703",4715:"8dfb128c",4799:"18f12793",4853:"7046da09",4941:"c792d462",5169:"9a63677c",5244:"240325d0",5295:"6f35f557",5354:"aeba84fb",5406:"9e4ad429",5413:"5ad68b42",5590:"81e44c7d",5605:"0cb3f831",5647:"99db9621",5811:"b84a6020",5831:"3b360413",6050:"f4b177ec",6091:"78bdd589",6167:"2cf43088",6440:"9177d15f",6576:"33ffb019",6583:"c0a0d864",6606:"09792c4b",6639:"c7794043",6795:"0b7df9a2",7008:"6a99b79c",7120:"ecbe7f2c",7207:"d5687ba7",7215:"14add631",7279:"420d9621",7531:"937990e1",7542:"b840d82f",7576:"ecf138d3",7706:"ff2c7cca",7805:"fd9368c3",7873:"ec6dc5c4",7918:"17896441",7920:"1a4e3797",7921:"c64b8421",7983:"cb12d9b9",8421:"e5aa2e7b",8537:"13f9d961",8645:"3365a9e9",8693:"b52e5d8d",8700:"fe4d0c3d",8773:"bb5d57b6",8798:"2b07918c",8819:"edb10261",8833:"c60f9f65",8845:"06bea203",8847:"3b7e54b4",9021:"409b673b",9117:"e2615d27",9134:"509f2ea4",9449:"d2c975d1",9484:"5ee690fc",9514:"1be78505",9517:"139f0dd3",9817:"14eb3368",9855:"0e89a207",9865:"f3b2d393",9887:"45ce19ba",9922:"7e516c75",9924:"df203c0f",9999:"a4dfa1d7"}[e]||e)+"."+{53:"fc80ed25",130:"c9e843a1",299:"cb5cf355",362:"669b6480",770:"666c2377",800:"94855e42",840:"7dfd9771",924:"8736aa8d",927:"37d4a00c",945:"7afccd58",952:"98a212dc",998:"eebfc85c",1074:"6fc496ec",1092:"6a4713de",1099:"9f16b5e1",1247:"5dfdf68d",1351:"3ea7593f",1426:"71674b4b",1441:"4a5c20ff",1572:"7ebdc609",1654:"f2e5d890",1694:"8d8e7884",1697:"60f7c7e3",1765:"bbf8e0e8",1837:"110e20cb",1872:"d0481e11",2050:"001f1829",2153:"ba5319f1",2163:"1c6a804c",2244:"c4f35040",2618:"2afc4922",2640:"7565d3dd",2731:"01d9ed00",2831:"fc8c5aed",2907:"11f5b3c0",3009:"125cb74f",3108:"6e1d1434",3157:"e1066268",3207:"43f6683b",3275:"bec08f3e",3447:"db005bf0",3466:"ad869df5",3605:"34bac101",3637:"f0eeff23",3664:"e8674569",3687:"ce88301b",3751:"465c883a",3786:"feb5464a",3864:"2ea92753",3877:"edf8973f",4027:"945299aa",4121:"16f30936",4134:"0534db3b",4195:"8f51117e",4200:"c1740670",4219:"b05fc5c0",4248:"360e6c79",4331:"5cf41d20",4414:"8467bdee",4502:"885ab7e7",4507:"59bf63a4",4514:"d1e2179b",4650:"84b2269e",4715:"01423c01",4799:"eca4f6de",4853:"3aaf63ac",4941:"4abaf11f",5169:"4f4aa057",5244:"418ae9b0",5295:"0bd177a2",5354:"db0b222d",5406:"17fdd2f9",5413:"ef3c436d",5590:"eef205bf",5605:"0bbf7690",5647:"86977d30",5811:"1c18e841",5831:"da161dd4",6050:"d55e6b4d",6091:"4ea7e967",6167:"e4910f97",6316:"2009feb9",6440:"9dbda4fa",6576:"c602af21",6583:"e50fa5e0",6606:"6060a3e7",6639:"ba82b1ba",6795:"5f8000b0",6945:"a569def7",7008:"df1b5be0",7120:"13620c1b",7207:"6c478ae9",7215:"316e2c03",7279:"a7b1a4cb",7531:"dc9c0439",7542:"d2e54469",7576:"93a412ab",7706:"58ca14b1",7724:"4286755f",7805:"cd3f9f7b",7873:"60577ed4",7918:"957336a6",7920:"dcd342f8",7921:"ae233fe9",7983:"db528504",8421:"ef84462c",8537:"b940ac19",8645:"0672b943",8693:"ba7e203c",8700:"3a405712",8773:"0d958107",8798:"e2871f9b",8819:"87a0b6a3",8833:"7c0f6cf1",8845:"01f84522",8847:"d7870ec0",9021:"246861c2",9117:"c2585def",9134:"61b2f7a5",9449:"00633d8b",9484:"579e0ecc",9487:"178ea76e",9514:"6fd0c9a9",9517:"86b16f74",9817:"96621dc4",9855:"ca99072c",9865:"dd9463de",9887:"938d0fd2",9922:"81ba2899",9924:"6fd6f28b",9999:"8d4c3a82"}[e]+".js",t.miniCssF=e=>{},t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),t.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),d={},t.l=(e,a,f,c)=>{if(d[e])d[e].push(a);else{var b,r;if(void 0!==f)for(var o=document.getElementsByTagName("script"),n=0;n{b.onerror=b.onload=null,clearTimeout(l);var c=d[e];if(delete d[e],b.parentNode&&b.parentNode.removeChild(b),c&&c.forEach((e=>e(f))),a)return a(f)},l=setTimeout(u.bind(null,void 0,{type:"timeout",target:b}),12e4);b.onerror=u.bind(null,b.onerror),b.onload=u.bind(null,b.onload),r&&document.head.appendChild(b)}},t.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.p="/",t.gca=function(e){return e={17896441:"7918","935f2afb":"53","0ae20921":"130","6ef4e5a9":"299","75f11010":"362",ba7840dc:"770","1328d02f":"800","5ee7ecad":"840",b39e9a29:"924","42b43273":"927","67a57efa":"945",a8b77297:"952",e5fc05e6:"998",dfa9382d:"1074","7f9f5c99":"1092","407fe916":"1099",d164ef0a:"1247","7eaafb14":"1351",d4ed38e0:"1441",f564aeae:"1572",ecd0154e:"1654","9be7a4ef":"1694","7a40f54e":"1697",ce321ae3:"1837","20e039ae":"1872","1843ef45":"2050",ff6cdf17:"2163","8440116e":"2244",b463a77c:"2618","9944d2ae":"2640","15878b2f":"2731","655aa231":"2831",f881cc1a:"2907","8a538aec":"3009","612d51b5":"3108","772c8612":"3157","22145d1a":"3207",b33fbe49:"3275",cafad187:"3447","4d6aece4":"3466",ff324314:"3605","48b2f688":"3637","369b6fa3":"3664","30084dac":"3687","3720c009":"3751",c8a2287d:"3786","8e6f5683":"3864","05bf1b0a":"3877",fa7b3a47:"4027","55960ee5":"4121","6d83c2a5":"4134",c4f5d8e4:"4195","92cda9bf":"4200","7f3cd917":"4219","2e2f252f":"4331","66b75e7a":"4414",dc8b40ff:"4502","7827587b":"4507",d44a7d50:"4514",a6709703:"4650","8dfb128c":"4715","18f12793":"4799","7046da09":"4853",c792d462:"4941","9a63677c":"5169","240325d0":"5244","6f35f557":"5295",aeba84fb:"5354","9e4ad429":"5406","5ad68b42":"5413","81e44c7d":"5590","0cb3f831":"5605","99db9621":"5647",b84a6020:"5811","3b360413":"5831",f4b177ec:"6050","78bdd589":"6091","2cf43088":"6167","9177d15f":"6440","33ffb019":"6576",c0a0d864:"6583","09792c4b":"6606",c7794043:"6639","0b7df9a2":"6795","6a99b79c":"7008",ecbe7f2c:"7120",d5687ba7:"7207","14add631":"7215","420d9621":"7279","937990e1":"7531",b840d82f:"7542",ecf138d3:"7576",ff2c7cca:"7706",fd9368c3:"7805",ec6dc5c4:"7873","1a4e3797":"7920",c64b8421:"7921",cb12d9b9:"7983",e5aa2e7b:"8421","13f9d961":"8537","3365a9e9":"8645",b52e5d8d:"8693",fe4d0c3d:"8700",bb5d57b6:"8773","2b07918c":"8798",edb10261:"8819",c60f9f65:"8833","06bea203":"8845","3b7e54b4":"8847","409b673b":"9021",e2615d27:"9117","509f2ea4":"9134",d2c975d1:"9449","5ee690fc":"9484","1be78505":"9514","139f0dd3":"9517","14eb3368":"9817","0e89a207":"9855",f3b2d393:"9865","45ce19ba":"9887","7e516c75":"9922",df203c0f:"9924",a4dfa1d7:"9999"}[e]||e,t.p+t.u(e)},(()=>{var e={1303:0,532:0};t.f.j=(a,f)=>{var d=t.o(e,a)?e[a]:void 0;if(0!==d)if(d)f.push(d[2]);else if(/^(1303|532)$/.test(a))e[a]=0;else{var c=new Promise(((f,c)=>d=e[a]=[f,c]));f.push(d[2]=c);var b=t.p+t.u(a),r=new Error;t.l(b,(f=>{if(t.o(e,a)&&(0!==(d=e[a])&&(e[a]=void 0),d)){var c=f&&("load"===f.type?"missing":f.type),b=f&&f.target&&f.target.src;r.message="Loading chunk "+a+" failed.\n("+c+": "+b+")",r.name="ChunkLoadError",r.type=c,r.request=b,d[1](r)}}),"chunk-"+a,a)}},t.O.j=a=>0===e[a];var a=(a,f)=>{var d,c,b=f[0],r=f[1],o=f[2],n=0;if(b.some((a=>0!==e[a]))){for(d in r)t.o(r,d)&&(t.m[d]=r[d]);if(o)var i=o(t)}for(a&&a(f);n