Customize your theme

You can customize the look and feel of your knowledge base theme.
  • Knowledge base theme code editor

    Each knowledge base theme has associated theme files. Theme files define the way that each of the pages will be displayed to your customer. They are written in a superset of HTML called JSX, which is a technology developed by Facebook. Learn more about JSX through the Offical JSX Documentation. You can also adjust styles through CSS. For a primer on CSS, check out this introduction.Who can access this feature?User typesContent administrators can access knowledge base settings.In this articleEnsure proper article formatting in your custom themeTrack analytics in your knowledge baseAdd suggested articles to your search barEnsure proper article formatting in your custom themeThe knowledge base offers a WYSIYWG or "What you see is what you get" editor, so your content creators can preview the appearance of an article during the authoring process. This works out-of-the-box with the default knowledge base themes, but if you plan to use a custom theme, your theme file will need to have a few particular settings in place to ensure that formatting publishes as expected.Before making the following changes, we strongly recommend backing up your current CSS file.  Copy and paste the existing styles.css file into another text editor to serve as a backup. Edit the theme by going to Settings > Knowledge Base > Themes, then select Edit for the theme you wish to edit.Note: If you created a theme using the visual theme builder, you can access the editor by selecting your theme, going to More Options , and selecting Go to Code Editor.From the Theme Files panel on the right, open the Pages category and select article.jsx.Add fr-view in front of article-body, as shown below.In the Globals category, open the styles.css file and add the following styles:.article-body blockquote {   border: none;   color: #222222;   padding-left: 4em; } body[dir="rtl"] blockquote {   padding-left: auto;   padding-right: 4em; } body[dir="rtl"] blockquote:before {   left: auto;   right: 1rem; }Here is an example of the code in the file:Use search to locate and remove the article-body img style block.Remove the Quill indent support styles. The entire block should be removed, up until the Search Page section.Track analytics in your knowledge baseYou can add Google Analytics to your knowledge base to help track its performance. You can add a Google Analytics script via a theme's GA.js global theme file. For more information, see Add Google Analytics to your knowledge base.Add suggested articles in your search barYou can add the ability to suggest articles in your knowledge base's search bar so that customer's can find the information they are searching for faster. For more information, see Show suggested articles in your knowledge base search bar.
  • Add Google Analytics to your knowledge base

    Often times you'll want to track the performance of your knowledge base. Google Analytics is a free service that will allow you to collect valuable insights into its usage. You'll be able to see how many people visit your knowledge base, which articles get the most amount of views, and how long they stay on the page. This can help you determine what content is and isn't working. You'll also be able to see how users navigate from page to page to understand what information they consume if they don't find their answer. Finally, Google Analytics can help you track user data related to location, browsers, and devices.Please note that EU regulators have issued decisions requiring website operators to remove Google Analytics from their websites due to concerns about data transfers to the United States. As such, we suggest that you monitor these developments, as the use of Google Analytics in the Europe Union and United Kingdom may not be deemed permitted by relevant regulators.Who can access this feature?User typesContent Administrators or Admins can access Knowledge base themes.In this articlePrerequisitesAdding Google Analytics to your KB ThemePrerequisitesYou must have an Analytics account before you can set this up in your Kustomer knowledge base theme.Adding Google Analytics to your knowledge baseOnce you have an account, you can add Google Analytics to your theme so you can start tracking insights.Go to Settingsand select Knowledge Base > Themes.Select the theme your knowledge base is using. If using the visual editor, go to More Optionsand select Go to Code Editor.Select Create New Draft and open the GA.js file found in the  Global section in the right panel. If your theme does not already have this file, select Add to create the file using the following options:Theme Target: GlobalFile Name: GAFile Type: JSEnter the following code in the editor:window.addEventListener('load', (event) => {  const script = document.createElement('script');   script.async = true;   script.src = "https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID";     document.getElementsByTagName('head')[0].appendChild(script);     window.dataLayer = window.dataLayer || [];   function gtag(){window.dataLayer.push(arguments);}   gtag('js', new Date());   gtag('config', 'GA_MEASUREMENT_ID'); });Enter your Google Analytics 4 property in the two lines that ask for GA_MEASUREMENT_ID. Note: If you set up your analytics before October 2020 and have a Universal Analytics property, you can use this ID instead.Select Save and Publish.
  • Show suggested articles in your knowledge base search bar

    You can configure your custom theme to include the ability to suggest articles as a customer types a search term in your knowledge base, making it faster for them to find the information that they are searching for and eliminating the need to submit a support request.Who can access this feature?User typesContent administrators can access knowledge base settings.To turn on this feature:Go to Settings > Knowledge Base > Themes.Select Edit  for the Default Theme.Select the SearchHeaderWithSuggestions.jsx component and copy it in its entirety.Go back to the Themes page and select Edit for the custom theme your knowledge base is using.Select Add.Select Component from the Theme Target drop-down menu and enter SearchHeaderWithSuggestions as the name.Select Create.Paste the file you originally copied into the new component.Select your homepage.jsx file.Replace your SearchHeader code block with the following:<SearchHeaderWithSuggestions         className="home-search-wrap" left={<h2><Snippet id="sn.kustomer.hello_how_can_we_help" /></h2>} org={org}       />Your file should resemble the following:Select your Form.css file.Add the following code block to style the list of suggestions./* ============================== Suggestion ============================== */.form-suggestion { width: auto; padding: 10px; margin-top: 10px; max-height: 200px; overflow-y: scroll; background: white; }.form-suggestion-article { margin-top: 10px; }.form-suggestion-article:hover { cursor:pointer; background-color: #e9eff5; padding: 10px; }.form-suggestion-article:hover a { color:#49a1d2; }.form-suggestion-label-divider { background-color: #e9eff5; height: 2px; width: auto; margin-top: 5px; }Note: If you are using a custom theme, you may want to modify this css to match your branding.Select Save.Now, whenever someone searches for a topic in your knowledge base, they will get a list of suggested articles that match their search term.
  • Customize your knowledge base theme

    Themes are a way for you to customize the look and feel of your knowledge base. Every organization has access to predesigned templates that come included with Kustomer. You can use as a template when creating your customized theme.If you have multiple brands configured, you can have a different theme for each knowledge base, ensuring that each brand delivers their own unique experience.Who can access this feature?User typesContent administrators can access knowledge base settings.In this article:Access theme templatesCreate a themeTheme SectionsCustomize a sectionCustomize the appearanceSave your themeUse theme versioningPreview your themeSet a theme for your Knowledge BaseAccess theme templatesKustomer provides your organization with a template that you can use as a basis when creating your own unique theme. You can customize and modify it to best fit your organization's branding. Note: Templates may receive updates over time. If you make adjustments to your customized theme, it will not update automatically.You can access your existing themes and available templates by going to Settings> Knowledge Base > Themes.The My Themes tab lists all of your themes in the order in which they were created. If you have multiple brands configured, you can see which theme is associated with a particular brand. For details on how to specify which brand the theme is used with, see Set a theme for your knowledge base.The Themes Library tab lists all of the available templates that you can use to create a new knowledge base theme.Create a themeYou can create a new theme that more closely matches your organization's personal branding using an available template.To create a new theme:Go to Settings> Knowledge Base > Themes.Select Add Theme and then select a template listed in the Themes Library.Enter a name for your theme and select Create.The theme builder opens. Here, you can customize the template as you wish by selecting each available section.Theme SectionsEach theme is divided into separate home page sections that you can customize to your liking. The following sections are available and may vary by theme:Page drop-down menu lets you choose if you want to customize the homepage, or the My Conversation and Conversation Details portal pages. For more information, see Knowledge base customer portal.Announcement is a horizontal banner that is located at the top of your home page that can be used to notify visitors of any important updates.Header is the main bar that appears at the top of all pages in your knowledge base. A company logo and navigation links are usually part of a website's header. Note: We recommend using an image that is approximately 40px in height and less than 250px in width.Search is the location where your visitors can search for articles in your knowledge base.Category Grid is all of the article categories in your knowledge base. For information on how to create categories, see Organize your knowledge base with categories.Featured Articles is a list of all of the articles that are marked as featured. You can display up to 20 articles in this section. For more information, see Feature articles on your knowledge base home page.Contact Us informs visitors of where they can go to get more support, if needed.Footer is the bar that appears at the bottom of all pages in your knowledge base. This section can contain copyright information and links to other frequently visited pages on your site.Customize a sectionYou can customize all of the sections in your knowledge base by selecting it under the Sections tab. Here, you can change elements of the section such as, text color, text message, URLs, and more. Any changes to a section are instantly made in the theme builder.Along with customizing a section visually, you can also turn off certain sections that you don't want to appear on your site at all, as well as reorder them.Customize the appearanceThe Design tab is where you can find specify the fonts and colors that are used throughout your knowledge base, so that you can ensure that your page fits your organization's branding.You can customize the appearance of the following elements:Font - Header, buttons, and body text, as well as their size.Colors - Font, buttons, and page.Save your themeWhen customizing your theme, you can select Save Draft at any time to save a draft of your progress. To publish your theme, go to the arrow and then select Save and Publish.Use theme versioningVersioning makes it simple to track all of your theme changes and switch between different versions, as needed. Every time you publish your theme, you create a new version of it. You can have up to 30 versions of a theme available. Once you reach this maximum, the oldest version is rewritten.To access a different version:When editing a theme, select More optionsand go to Latest Version.Select a version from the menu that opens.You can instantly compare versions to see what changed between them, or edit a previous version of your theme by selecting Create New Draft.Preview your themeTo see your changes at any time, select Preview Theme and then select the brand using the theme to launch a new window that previews your theme's home page, which is the root page of your knowledge base. By default, the Default brand is always shown. Set a theme for your knowledge baseBy default, our system sets the default theme as the active theme for your knowledge base, regardless of whether or not you have configured multiple brands. If you create a customized theme for one of more of your brands, you can select the theme to use by:Going to to Settingsand selecting Knowledge Base > Configuration.If you have multiple brands configured, select the brand for which you are setting a theme from the drop-down menu.Select the theme you want to use from the Theme drop-down menu.Select Save Changes.Your theme is now applied to your Knowledge Base and all changes can be viewed immediately.
  • Feature articles on your knowledge base home page

    You can draw attention to important articles and make it easier for your customers to find answers to common questions by adding them to the Featured Articles section of your knowledge base's home page. Highlighting articles that are commonly referenced by your customers can help them more quickly find the information they're searching for and encourage them to self serve and solve their own request.Who can access this feature?User typesContent adminstrators can access the Knowledge base page.In this articleAdd an article to the Featured Articles sectionManage your featured articlesTurn off the Featured Articles sectionAdd an article to the Featured Articles sectionYou can promote articles on the Featured Articles section of your knowledge base home page.Note: An article must be added to a brand in the Manage Brands and Categories section first.Go to Settings> Knowledge Base > Articles.Select the article you want to add to your home page.Select Feature this article and turn on brands for the home page(s) you want to display it on.Your published article is now shown on your home page.Featured articles are also displayed in your chat widget if you selected one of the formats that includes the knowledge base.Manage your featured articlesYou can view a list of your featured articles by selecting the Featured Articles tab on the Articles page and then selecting the brand you want to view. Here, you can organize the order that the articles will appear by dragging and dropping them to a new location on the list.Turn off the Featured Articles sectionBy default, the Featured Articles section is visible on your knowledge base theme. You can turn this section off at any time.To turn off the Featured Articles section:Go to Settings> Knowledge Base > Themes.Select your theme under the My Themes tab.Turn off the toggle for Featured Articles.Select Save.
  • Add fonts to your knowledge base theme

    Along with customizing your knowledge base theme, you can also use custom or Google fonts that better match your organization's branding.Who can access this feature?User typesContent administrators can access knowledge base settings.In this article:Add a custom fontAdd a Google fontAdd a custom fontYou can use a custom font in your knowledge base theme.Download and host the custom font files.Go to Settings > Knowledge Base > Theme.Select the theme your knowledge base is using. If using the visual editor, go to More Options and select Go to Code Editor.Select Create New Draft.Select Add and create a file using the following options:Theme Target: GlobalFile Name: fontFile Type: JSSelect Create.Copy this script and paste it into the new file.document.addEventListener("DOMContentLoaded", function(event) {  var scriptWOFF = document.createElement("script");   var scriptWOFF2 = document.createElement("script");     scriptWOFF.src = "https://insert-hosted-custom-font-link";   scriptWOFF2.src = "https://insert-hosted-custom-font-link";     document.head.appendChild(scriptWOFF);   document.head.appendChild(scriptWOFF2); });Open the styles.css file and make the following changes:Add your font-face declaration:@font-face {  font-family: "Name of Font";   src: url("https://insert-hosted-custom-font-link.woff2") format("woff2"),        url("https://insert-hosted-custom-font-link.woff") format("woff"); }Add your custom font override in your CSS declarations (all * and html selectors):* {  font-family: "Name of Font" !important; } html {   font-family: "Name of Font" !important; }If you created your theme using the Midtown theme as the default, update your root CSS font variables to use the custom font override below.:root {  --headingsAndButtonsFont: "Name of Font" !important;   --bodyTextFont: "Name of Font", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,     Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif !important; }Note: If you are updating your theme and don't see the change take effect, this could be due to your Kustomer knowledge base not having access to the font resource you are hosting. When you host this font, we recommend either of the following best practices to avoid any resulting CORS errors:Host the file on a public server that does not limit access.Add your knowledge base as a permitted domain to your CORS policy.Add a Google fontYou can use a Google font in your knowledge base theme.Go to Settings > Knowledge Base > Theme.Select the theme your knowledge base is using. If using the visual editor, go to More Options  and select Go to Code Editor.Select Create New Draft.Select Add and create a file using the following options:Theme Target: GlobalFile Name: fontFile Type: JSSelect Create.Copy this script and paste it into the new file.document.addEventListener("DOMContentLoaded", function(event) {  var link = document.createElement("link");   link.rel = "stylesheet";   link.href = "https://fonts.googleapis.com/css?family=Font+Name";   document.head.appendChild(link); });Open the styles.css file and make the following changes: Add your custom font override in your CSS declarations:* {     font-family: "Name of Font" !important;  } html {   font-family: "Name of Font" !important; }If you created your theme using the Midtown theme as the default, update your root CSS font variables to use the custom font.:root {   --headingsAndButtonsFont: "Name of Font" !important;   --bodyTextFont: "Name of Font", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,     Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif !important; }
  • Upgrade your knowledge base theme

    If you created your knowledge base using a theme that was released prior to the Midtown 2.0 version, you may need to manually update your theme files to get you up to date with the latest features. Who can access this feature?User typesContent administrators can access the Themes page.To make these changes, select Edit  for the theme, go to More Optionsand select Go to Code Editor.Select Create New Draft to begin making the necessary edits. Once you're done updating your themes, select Save and Publish.The following table shows the theme versions that require manual updates to get those features. Theme versionFeature introduced1.0.6Article feedback survey1.0.17Capture search article click results1.0.18Lang parameters to hrefs for proper redirects of article and category pages1.0.20Portal for end customers and internal usersPlease follow the procedures listed for each version to get yourself up to date.Tip: To find out what theme version you are on, please go to Settings > Knowledge Base > Themes and hover over the colored label. If you are on a version that requires these updates, you will see the base version you are on.Upgrade to version 1.0.6If your theme is on version 1.0.6, please follow the procedures for Version 1.06, 1.0.17, 1.0.18, and 1.0.20 to get you up to date. Open the the form.css file and copy and paste the following class under the TextArea section:.form-field-textarea-charCount {   display: flex;   justify-content: flex-end;   width: 100%;   margin-left: 10px;   margin-top: 5px;   color: #9A9A9A;} .form-field-textarea-charCountOver {   color: red; }Open the survey.css file and add the following class at the bottom:/*  ==============================   Article Survey   ============================== */ .article-survey-form {   width: 50%; } .article-survey-wrapper {   margin: 50px auto; } .article-survey-rating-wrapper, .article-survey-error-wrapper, .article-survey-success-wrapper {   display: flex;   align-items: center; } .article-survey-rating-button {   margin-left: 10px;   font-weight: bold;   width: 80px; } .article-survey-error-wrapper {   margin: 15px 0;   color: #D1261E;   line-height: 20px; } .article-survey-error-message {   margin-left: 5px; } .article-survey-success-icon {   color: #74cc98;   font-size: 22px;   margin-right: 10px; } .article-survey-suggested-reasons-wrapper, .article-survey-writtenFeedback {   margin-left: -10px; } .article-survey-suggested-reasons-wrapper > label, .article-survey-writtenFeedback label {   font-weight: bold;   margin-bottom: 15px; } .article-survey-suggested-reason .form-field-checkbox-input:checked + label::after {   top: 0 } .article-survey-suggested-reason label {   line-height: 25px; } .article-survey-writtenFeedback textarea {   resize: none;   width: 100%; } @media only screen and (max-width: 767px) {   .article-survey-form {     width: 100%;   }   .article-survey-rating-wrapper {     flex-direction: column;     align-items: flex-start;   }   .article-survey-rating-question {     margin-bottom: 10px;   }   .article-survey-rating-button[name='positiveButton'] {     margin-left: 0;   } }Open the article.jsx file and copy and paste the following line directly above the /articleclosing tag.<ArticleSurveyForm articleId={article.articleId} lang={article.lang} orgName={org.name} settings={_.get(org, 'settings.articleSurvey')} />Feedback surveys will now appear in your articles.Upgrade to version 1.0.17If your theme is on version 1.0.17, please follow the procedures for Version 1.0.17, 1.0.18, and 1.0.20 to get you up to date.  Open the SearchItem.jsx file and replace its contents with the following code.<React.Fragment>   {(() => {     class SearchItem extends React.Component {       constructor(props) {         super(props);         this.handleClickSearchResult = this.handleClickSearchResult.bind(this);       }       handleClickSearchResult(e) {         e.preventDefault();         const { org, domain, searchBucketId, searchId, lang, articleId, href } =           this.props;         if (searchBucketId && searchId) {           const url = `https://${org.name}.api.${domain}/p/v1/kb/article-search-buckets/${searchBucketId}/searches/${searchId}`;           const articleSearchData = {             lang,             articleId,           };           return fetch(url, {             method: "PATCH",             body: JSON.stringify(articleSearchData),             headers: {               "Content-type": "application/json",             },           })             .then(() => {               window.location.href = href;             })             .catch((err) => {               console.error(err);               window.location.href = href;             });         } else {           window.location.href = href;         }       }       render() {         return (           <div className="article-item-search">             <a               href="#"               onClick={this.handleClickSearchResult}               className="article-item-header-wrap"             >               <h2 className="article-item-header bold">{title}</h2>             </a>             <div className="article-item-time">               {publishedAt &&                 plugins.moment(publishedAt).format("MMMM D YYYY h:mma")}             </div>             <div className="article-item-body">               <div                 dangerouslySetInnerHTML={{                   __html: (body || "")                     .replace(/<(?:.|\n)*?>/gm, "")                     .slice(0, 160)                     .concat("..."),                 }}               />             </div>           </div>         );       }     }     SearchItem.defaultProps = {       href: "",       title: "",       publishedAt: "",       body: "",       org: {},       domain: "",       searchBucketId: "",       searchId: "",       lang: "",       articleId: "",     };     return React.createElement(SearchItem, {       href,       title,       publishedAt,       body,       org,       domain,       searchBucketId,       searchId,       lang,       articleId,     });   })()} </React.Fragment>;Open the search.jsx file and replace the SearchItem  with the following code.  <SearchItem           key={`${el.title}-${el.publishedAt}`}      {...el}      publishedAt={el.publishedAt}      org={org}      domain={domain}    />Open the tag.jsx file and replace the SearchItem  with the following code.<SearchItem           key={el.title}    {...el}    publishedAt={el.publishedAt}    org={org}    domain={domain}    searchBucketId={undefined}    searchId={undefined}   />Now, your Knowledge Base report will show the amount of clicks an article receives after a user performs a search.Upgrade to version 1.0.18If your theme is on version 1.0.18, please follow the procedures for Version 1.0.18, and 1.0.20 to get you up to date.  Open the styles.css file, find .category-wrap-left-aligned::after { and change width: 31% to width: 33%.Open the ArticleItem.jsx file, find <a href={/${slug}-${hash}} className="article-item-header-wrap"> on line 2 and replace that code with: <a href={`/${lang}/${slug}-${hash}`} className="article-item-header-wrap">Open the CategoryBlock.jsx file, find <a href={`/categories/${slug}-${hash}`} className="category-block-item"> on line 1 and replace that code with:<a href={`/${lang}/categories/${slug}-${hash}`} className="category-block-item">Open the SubcategoryItem.jsx file, find href={`/categories/${slug}-${hash}`}  on line 2 and replace that code with:href={`/${lang}/categories/${slug}-${hash}`}Open the ArticleBreadcrumbs.jsx file, find the two references to href={`/categories/${bc.slug}-${bc.hash}`} and replace both of them with: href={`/${bc.lang}/categories/${bc.slug}-${bc.hash}`}Upgrade to version 1.0.20If your theme is on version 1.0.20, please follow this procedure to get you up to date. Create the following new files using the instructions for each one as described below:portal.cssconversation.jsxconversations.jsxlogin.jsxConversationBreadcrumbs.jsxConversationDetails.jsxConversationStatus.jsx401.jsxOnce you are done making all of the changes, Save and Publish your theme.Note: File names are case sensitive. Please be sure to name each file using the exact letter case shown below.Create new portal.css fileSelect Add and use the following options to create the new file:Theme Target: GlobalFile Name: portalFile Type: CSSCopy and paste the following code in the new file:/*  ==============================   Conversations   ============================== */ .container-header-conversations, .container-header-conversation {   text-align: left !important;   max-width: 100% !important; } .container-header-conversation {   display: flex;   align-items: center; } .nav-search-conversations {   padding: 0 !important; } .form-control-search-wrap-conversations {   width: 100% !important; } .conversations-icon-close {   position: absolute;   right: 1rem;   top: 12px;   font-size: 1.5rem;   color: #8b97a1;   cursor: pointer; } .container-status-filter {   max-width: 100%;   display: flex;   margin: 25px 0; } @media only screen and (min-width: 768px) {   .container-status-filter {     max-width: 300px;   } } .conversations-container {   width: 100%;   padding: 1.5rem 1rem; } @media only screen and (max-width: 768px) {   .conversations-container .container-home {     margin: 0 auto;     padding: 0 10px;   } } .conversations-container .container {   margin: 0 auto; } .conversations-breadcrumb {   display: flex;   flex-wrap: wrap;   margin-bottom: 30px; } .conversations-breadcrumbs-link {   margin-bottom: 5px; } .breadcrumb-chevron-right-icon {   margin: 0 5px;   color: #9a9a9a; } .status-filter {   cursor: pointer;   padding: 10px;   border: 1px solid #e3e3e3;   display: flex;   align-items: center;   justify-content: center;   width: 100%;   text-align: center;   color: unset !important; } .status-filter:first-of-type {   border-right: none;   border-radius: 80px 0px 0px 80px; } .status-filter:last-of-type {   border-left: none;   border-radius: 0px 80px 80px 0px; } .status-filter-active {   background-color: #e3e3e3; } .conversations-list, .conversations-list-item {   list-style: none; } .conversations-list {   margin: 0;   padding: 0; } .conversations-list-item {   position: relative;   padding: 15px; } .conversations-list-item:nth-child(even) {   background-color: rgba(244, 243, 248, 0.4);   border-radius: 12px; } .conversations-list-item-link-wrapper {   font-weight: bold;   display: flex;   align-items: center;   justify-content: space-between; } .conversations-list-item-left, .conversations-list-item-right {   display: flex;   align-items: center; } .conversations-list-item-date {   color: #9a9a9a;   font-size: 12px; } .conversations-list-item-status {   border-radius: 80px;   padding: 5px 10px;   font-weight: bold;   font-size: 12px;   margin: 0 10px; } .conversations-list-item-status.open {   color: #15cc70;   background-color: rgba(21, 204, 112, 0.1); } .conversations-list-item-status.done {   color: #767676;   background-color: rgba(118, 118, 118, 0.1); } .channel-icon-container {   font-size: 12px;   background-color: #fff;   border: 1px solid #e3e3e3;   border-radius: 50%;   padding: 5px;   position: relative;   margin-right: 10px;   display: flex;   align-items: center;   justify-content: center; } .conversations-empty {   display: flex;   align-items: center;   flex-direction: column;   justify-content: center;   text-align: center;   padding: 20px 0 40px;   background: rgba(244, 243, 248, 0.4);   border-radius: 12px; } .conversations-empty-text {   color: #767676; } /*   ==============================   Conversation Details   ============================== */ .thread-container {   width: 100%; } .thread-container-content {   margin: 20px; } .thread-divider {   margin: 20px 0;   height: 1px;   background-color: #e3e3e3;   border: none; } .conversation-detail {   display: flex;   justify-content: space-between;   width: 100%; } .details-container {   width: 320px;   border-left: 1px solid #e3e3e3;   padding-left: 20px; } .details-header {   display: flex;   align-items: center;   justify-content: space-between;   background-color: #fff;   width: 100%;   padding: 0;   pointer-events: none; } .details-title {   font-weight: bold;   font-size: 18px;   font-family: "Helvetica";   font-family: var(--headingsAndButtonsFont);   color: #0a3355;   color: var(--headingsColor); } .detail-expand-icon {   display: none; } .attributes-container {   display: flex;   flex-direction: column; } .attribute-container {   display: flex;   flex-direction: column;   padding: 10px 5px; } .attribute-label {   color: #767676;   font-weight: bold;   margin-bottom: 5px; } @media only screen and (max-width: 768px) {   .thread-container-content {     margin: 0;   }   .details-container {     width: 100%;     margin: 20px auto;     border-left: none;     padding-left: 0;     cursor: pointer;   }   .details-header {     background-color: #fafafa;     border-radius: 12px;     padding: 10px;     pointer-events: unset;   }   .conversation-detail {     display: block;   }   .details-title {     font-size: 12px;   }   .detail-expand-icon {     display: block;   }   .attribute-container {     padding: 10px;   }   .attributes-container.close {     display: none;   }   .attributes-container.open {     display: flex;     flex-direction: column;   } } /*   ==============================   Conversation Message   ============================== */ .messages-container {   overflow: auto;   height: 500px;   margin-bottom: 20px;   -ms-overflow-style: none; /* IE and Edge */   scrollbar-width: none; /* Firefox */ } .messages-container::-webkit-scrollbar {   display: none; } .message-container {   display: flex;   margin: 0 20px 20px;   overflow: visible; } .message-container.out {   flex-direction: row; } .message-container.in {   flex-direction: row-reverse; } .message-text {   font-size: 16px;   font-size: var(--baseSizeFont);   word-break: break-word; } .message.out {   margin-left: 10px; } .message.in {   margin-right: 10px; } .message-bubble {   max-width: 500px;   width: 100%;   padding: 10px;   font-size: 14px;   line-height: 18px; } .message-bubble img {   max-width: 100%;   max-height: 100%;   object-fit: cover;   overflow: hidden; } .message-bubble.out {   background-color: #fff1f0;   border-radius: 0px 10px 10px 10px; } .message-bubble.in {   background-color: #f4f3f8;   border-radius: 10px 0 10px 10px; } .message-avatar {   height: 40px;   width: 40px;   min-width: 40px;   border-radius: 50%; } .message-avatar.out {   background-color: #fff1f0; } .message-avatar.in {   background-color: #f4f3f8; } .message-timestamp {   margin-right: 5px; } .message-meta-container, .message-icon-container {   display: flex;   position: relative; } .message-icon {   color: #15cc70; } .message-icon.pending {   color: #767676; } .message-icon.failed {   color: #ff655d; } .message-meta {   margin-top: 10px;   font-size: 14px;   color: #767676;   display: flex;   align-items: baseline; } .message-meta.in {   justify-content: flex-end; } .message-meta.out {   justify-content: flex-start; } .message-channel-wrapper {   display: flex;   align-items: baseline;   font-weight: bold; } .message-channel {   margin-left: 5px;   text-transform: capitalize; } .messages-see-more-container {   display: flex;   justify-content: center;   margin: 0 auto 20px; } .messages-see-more-button {   padding: 10px;   cursor: pointer; } @media only screen and (max-width: 768px) {   .message-container {     margin: 0 0 20px 0;   } } .conversation-message-attachments-container {   margin-top: 10px; } /*   ==============================   Conversation Reply   ============================== */ .conversation-reply-form {   width: 100%;   border: 1px solid #e3e3e3;   border-radius: 12px;   padding: 10px; } .conversation-reply-textarea {   margin-bottom: 0; } .conversation-reply-textarea textarea {   width: 100%;   border: none;   resize: vertical;   min-height: 80px;   margin: 0;   padding: 0;   line-height: unset; } .conversation-reply-charLimit {   margin-left: 0; } .conversation-reply-textarea label {   display: none; } .conversation-actions-container {   display: flex;   align-items: center;   width: 100%;   justify-content: flex-end;   margin-top: 10px; } .conversation-action-send-button {   display: flex;   align-items: center; } .conversation-action-send-text {   margin-left: 10px; } /*   ==============================   Conversation Editor   ============================== */ .conversation-editor-container {   min-height: 200px; } .conversation-editor-attachments-container {   border-left: 1px solid #e3e3e3;   border-right: 1px solid #e3e3e3;   padding: 15px 15px 0; } .conversation-editor-quill-container {   padding-bottom: 40px;   height: 160px; } #portal-editor {   border-bottom: none;   border-left: 1px solid #e3e3e3;   border-right: 1px solid #e3e3e3; } .conversation-editor-actions-container {   display: flex;   justify-content: flex-end;   align-items: center;   padding-bottom: 10px;   padding-top: 10px;   border-left: 1px solid #e3e3e3;   border-right: 1px solid #e3e3e3;   border-bottom: 1px solid #e3e3e3; } .conversation-action-attachment {   margin-right: 24px; } .conversation-action-attachment-label {   cursor: pointer; } .conversation-action-attachment-label-disabled {   cursor: not-allowed; } .conversation-action-send-button {   margin-right: 10px;   background-color: #52aaff; } .conversation-editor-attachments-container   .conversation-attachments-container-end-user {   justify-content: flex-start; } /*   ==============================   Conversation Attachments (plural)   ============================== */ .conversation-attachments-container-end-user {   display: flex;   justify-content: flex-end;   align-items: center;   min-height: 90px; } .conversation-attachments-container-agent {   display: flex;   justify-content: flex-start;   align-items: center;   min-height: 90px; } @media only screen and (max-width: 768px) {   .conversation-attachments-container-end-user {     flex-direction: column;   }   .conversation-attachments-container-agent {     flex-direction: column;   } } /*   ==============================   Conversation Attachment (singular)   ============================== */ @media only screen and (max-width: 768px) {   .conversation-attachment-container {     margin-bottom: 10px;   } } .conversation-reply-form-file-attachment {   width: 140px;   overflow: hidden;   display: flex;   flex-direction: column;   align-items: center;   margin-right: 10px;   background: #fafafa;   border: 1px solid #e3e3e3;   border-radius: 12px;   padding: 10px;   cursor: pointer;   background-size: cover; } .conversation-attachment-modal-file {   width: 400px;   height: 300px;   overflow: hidden;   display: flex;   flex-direction: column;   justify-content: center;   background: #fafafa;   padding: 10px;   cursor: pointer; } .conversation-reply-form-file-attachment-delete {   align-self: flex-end; } .conversation-reply-form-file-attachment-delete-icon {   color: #767676;   cursor: pointer; } .conversation-reply-form-file-attachment-wrapper-modal {   width: 100%;   display: flex;   justify-content: center; } .conversation-reply-form-file-attachment-wrapper {   width: 100%;   display: flex; } .conversation-reply-form-file-attachment-type {   background-color: #ff655d;   border-radius: 6px;   flex: 0 0 auto;   font-size: 10px;   font-weight: 700;   color: white;   padding: 5px; } .conversation-reply-form-file-attachment-name-wrapper {   display: flex;   flex-direction: column;   justify-content: center; } .conversation-reply-form-file-attachment-name-modal {   overflow: hidden;   text-overflow: ellipsis;   white-space: nowrap;   margin-left: 4px;   width: 100%; } .conversation-reply-form-file-attachment-name {   overflow: hidden;   text-overflow: ellipsis;   white-space: nowrap;   margin-left: 4px;   width: 80px; } .conversation-reply-form-file-attachment-size-wrapper-modal {   display: flex;   justify-content: center; } .conversation-reply-form-file-attachment-size-wrapper {   align-self: flex-start; } .conversation-reply-form-file-attachment-size-modal {   display: inline-block;   align-self: flex-start;   font-size: 10px;   margin-top: 4px; } .conversation-reply-form-file-attachment-size {   display: inline-block;   align-self: flex-start;   font-size: 16px;   margin-top: 4px; } /*   ==============================   Navigation   ============================== */ .nav-account-container {   position: relative; } .nav-account {   display: flex;   align-items: center;   padding-left: 20px;   margin-left: 10px;   border-left: 1px solid #efeef6;   cursor: pointer; } .nav-account-name {   margin: 0 10px; } .nav-account-link {   cursor: pointer; } .nav-avatar {   height: 20px;   width: 20px;   border-radius: 50%;   border: 1px solid #e3e3e3; } .nav-account-list-container {   position: absolute;   display: flex;   flex-direction: column;   background-color: #fff;   padding: 20px;   width: 200px;   right: 0;   top: calc(100% + 1rem);   box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08);   border-radius: 0px 0px 12px 12px; } .nav-account-list-item {   list-style: none;   margin-bottom: 20px; } .nav-menu-icon {   font-size: 22px; } @media only screen and (max-width: 768px) {   .nav-avatar {     margin-right: 10px;   }   .nav-account-container.desktop {     display: none;   }   .nav-account-list-container {     border: none;     border-radius: unset;     z-index: 1;     position: absolute;     width: 100vw;     height: 100vh;     right: -18px;     margin-top: -0.5rem;     box-shadow: 0 2px 6px 0 rgb(167 167 167 / 50%);     padding: 0;   }   .nav-account-list-item {     margin-bottom: 0;   }   .nav-account-name {     display: none;   } } /*   ==============================   Login   ============================== */ .login-buttons-container {   display: flex;   flex-direction: column;   align-items: center;   padding: 15px; } .login-buttons-form {   padding: 20px;   border: 2px solid #ececec;   border-radius: 16px; } .login-header {   margin-bottom: 20px; } .login-button {   border: 1px solid #e3e3e3;   border-radius: 12px;   display: flex;   align-items: center;   width: 300px;   justify-content: center;   padding: 15px;   margin-bottom: 15px; } .login-button-text {   margin-left: 10px; } /*   ==============================   Utils   ============================== */ .showInMobile {   display: none; } .hideInMobile {   display: block; } @media only screen and (max-width: 768px) {   .showInMobile {     display: block;   }   .hideInMobile {     display: none;   } } /*   ==============================   Modal (shared, generic)   ============================== */ .shared-modal {   margin: auto;   padding: 0;   border: 0; } .shared-modal-content-conversation-attachment {   display: flex; } .shared-modal-title-bar {   display: flex;   justify-content: space-evenly;   position: absolute;   right: 0;   padding-top: 10px;   padding-right: 10px; } .shared-modal-action-icon {   font-size: 24px;   color: #767676;   cursor: pointer; }Create new conversation.jsx fileSelect Add and use the following options to create the new file:Theme Target: PagePage Type: ConversationCopy and paste the following code in the new file:<React.Fragment>   {(() => {     class Conversation extends React.PureComponent {       constructor(props) {         super(props);         this.state = {           expandMenu: false,           messages: [],           links: {},         };         this.handleToggleMenu = this.handleToggleMenu.bind(this);         this.scrollToBottom = this.scrollToBottom.bind(this);         this.renderConversationDetails = this.renderConversationDetails.bind(this);         this.handleClickShowMore = this.handleClickShowMore.bind(this);         this.handleClickSend = this.handleClickSend.bind(this);       }       componentDidMount() {         const { data } = this.props;         const conversation = _.get(data, "conversation");         const conversationMessages = _.get(conversation, "messages.data");         const conversationMessagesLinks = _.get(conversation, "messages.links");         this.setState({           messages: conversationMessages,           links: conversationMessagesLinks         });         window.addEventListener("load", () => {           this.scrollToBottom();         });       }       scrollToBottom() {         this.messagesEnd.scrollIntoView({ behavior: "smooth", block: "end" });       }       handleToggleMenu() {         this.setState((prevState) => ({           expandMenu: !prevState.expandMenu,         }));       }       handleClickSend(msg) {         this.setState((prevState) => ({           messages: prevState.messages.concat(msg),         }), () => {           setTimeout(() => {             this.scrollToBottom();           }, 300);         });       }       handleClickShowMore({ prevMessages, links }) {         this.setState((prevState) => ({           messages: prevMessages.concat(prevState.messages),           links         }));       }       renderMoreButton() {         const { links } = this.state;         const { org } = this.props;         const conversationMessagesLinkNext = _.get(links, "next");         if (!conversationMessagesLinkNext) return null;         return (           <div className="messages-see-more-container">             <ConversationShowMore                onClick={this.handleClickShowMore}                nextLink={conversationMessagesLinkNext}               orgName={org.name}             >               <Snippet id="sn.kustomer.themebuilder.show_more_text"/>...             </ConversationShowMore>           </div>         );       }       renderConversationDetails(className) {         const { expandMenu } = this.state;         const { data } = this.props;         const conversationPage = _.get(data, "org.manifest.pages.conversation");         const conversationVariables = _.get(conversationPage, "variables");         const conversation = _.get(data, "conversation");         return (           <ConversationDetails             conversation={conversation}             expandMenu={expandMenu}             onExpand={this.handleToggleMenu}             className={className}             data={conversationVariables}           />         );       }       renderHeader() {         const { data } = this.props;         const conversationsPage = _.get(data, "org.manifest.pages.conversations");         const conversationsVariables = _.get(conversationsPage, "variables");         const conversation = _.get(data, "conversation");         const conversationName = _.get(conversation, "name");         const conversationStatus = _.get(conversation, "status");         return (           <div className="container-header container-header-conversation">             <h2 className="header-primaryMessage conversations-primaryText">               {conversationName}             </h2>             <ConversationStatus               status={conversationStatus}               data={conversationsVariables}             />           </div>         );       }       renderConversation() {         const { org, data } = this.props;         const { messages } = this.state;         const conversationPage = _.get(data, "org.manifest.pages.conversation");         const conversationVariables = _.get(conversationPage, "variables");         const sendButtonText = _.get(conversationVariables, "sendButtonText.value", "Send");         const messageFailedText = _.get(conversationVariables, "messageFailedText.value");         const messagePendingText = _.get(conversationVariables, "messagePendingText.value");         const replyBoxPlaceholderText = _.get(conversationVariables, "replyBoxPlaceholderText.value");         const conversation = _.get(data, "conversation");         const customer = _.get(org, "settings.customer");         const snippetsText = {           sendButtonText: eval(sendButtonText),           messageFailedText: eval(messageFailedText),           messagePendingText: eval(messagePendingText),           replyBoxPlaceholderText: eval(replyBoxPlaceholderText),         };         return (           <div className="thread-container">             {this.renderHeader()}             {this.renderConversationDetails("showInMobile")}             <div className="thread-container-content">               <div className="messages-container">                 {this.renderMoreButton()}                 <div className="messages">                   {messages.map((msg, idx) => (                     <ConversationMessage                       key={`${msg.id}-${idx}`}                       message={msg}                       data={conversationVariables}                       customer={customer}                       snippetsText={snippetsText}                     />                   ))}                 </div>                 <div id="thread-bottom" ref={(el) => (this.messagesEnd = el)} />               </div>               <hr className="thread-divider" />               <ConversationReply                 conversation={conversation}                 snippetsText={snippetsText}                 settings={org.settings}                 onClick={this.handleClickSend}                 orgName={org.name}               />             </div>           </div>         );       }       render() {         const { data } = this.props;         const conversation = _.get(data, "conversation");         const conversationName = _.get(conversation, "name");         const lang = _.get(data, "org.settings.lang");         return (           <main className="main-layout">             <Announcement               data={findSection(data.org.manifest, "announcement")}               template={data.template}             />             <Nav               data={findSection(data.org.manifest, "header")}               settings={org.settings}             />             <div className="conversation-detail-container">               <div className="conversations-container">                 <ConversationBreadcrumbs                   view="detail"                   name={conversationName}                   lang={lang}                 />                 <section className="container conversation-detail">                   {this.renderConversation()}                   {this.renderConversationDetails("hideInMobile")}                 </section>               </div>             </div>             <ContactUs data={findSection(data.org.manifest, "contactUs")} />             <Footer data={findSection(data.org.manifest, "footer")} />           </main>         );       }     }     Conversation.defaultProps = {       data: {},       org: {},       domain: ''     };     return React.createElement(Conversation, {       data,       org,       domain     });   })()} </React.Fragment>;Create new conversations.jsx fileSelect Add and use the following options to create the new file:Theme Target: PagePage Type: ConversationsCopy and paste the following code in the new file:<React.Fragment>   {(() => {     class Conversations extends React.PureComponent {       constructor(props) {         super(props);         this.state = {           query: _.get(props, 'data.query.q', '')         }         this.renderConversationListItem = this.renderConversationListItem.bind(this);         this.handleChange = this.handleChange.bind(this);       }       handleChange(e) {         const query = e.target.value;         this.setState({           query,         });       }       renderHeader() {         const { data } = this.props;         const { query } = this.state;         const conversationsPage = _.get(data, "org.manifest.pages.conversations");         const primaryText = _.get(conversationsPage, "variables.primaryText.value");         const secondaryText = _.get(conversationsPage, "variables.secondaryText.value");         const lang = _.get(data, "org.settings.lang");         const pageReset = replaceUrlParam(window.location.search, 'page', '1');         const conversationsHref = `/${lang}/conversations${replaceUrlParam(pageReset, 'q', '')}`;         return (           <div className="container-header container-header-conversations">             <h2 className="header-primaryMessage conversations-primaryText">               {eval(primaryText)}             </h2>             <h3 className="header-secondaryMessage conversations-secondaryText">               {eval(secondaryText)}             </h3>             <div className="nav-search nav-search-conversations">               <form                 className="form-control-search-wrap form-control-search-wrap-conversations"                 action="/conversations?q="                 method="GET"               >                 <label htmlFor="searchInput" hidden>                   <Snippet id="sn.kustomer.search" />                 </label>                 <i className="icon-search mdi mdi-magnify" aria-hidden="true" />                 <input                   className="form-control form-control-search"                   id="searchInput"                   name="q"                   placeholder={snippet("sn.kustomer.themebuilder.search_bar_label")}                   aria-label={snippet("sn.kustomer.themebuilder.search_bar_label")}                   value={query}                   onChange={this.handleChange}                 />                 {query && (                   <a href={conversationsHref}>                     <i className="conversations-icon-close mdi mdi-close" aria-hidden="true" />                   </a>                 )}               </form>             </div>           </div>         );       }       renderStatusFilters() {         const { data } = this.props;         const conversationsPage = _.get(data, "org.manifest.pages.conversations");         const statusFilterBackgroundColor = _.get(conversationsPage, "variables.statusFilterBackgroundColor.value");         const statusFilterSelectedBackgroundColor = _.get(conversationsPage, "variables.statusFilterSelectedBackgroundColor.value");         const conversationStatus = _.get(data, 'query.conversationStatus', undefined);         const allSelected = !conversationStatus;         const openSelected = conversationStatus === "open";         const doneSelected = conversationStatus === "done";         const lang = _.get(data, "org.settings.lang");         const pageReset = replaceUrlParam(window.location.search, 'page', '1');         return (           <div className="container-status-filter">             <a                href={`/${lang}/conversations${replaceUrlParam(pageReset, 'conversationStatus', '')}`}                className={`status-filter ${                 allSelected                   ? "status-filter-active conversations-statusFilterSelectedBackgroundColor"                   : "conversations-statusFilterBackgroundColor"               }`}               style={{                 backgroundColor: allSelected                   ? statusFilterSelectedBackgroundColor                   : statusFilterBackgroundColor,               }}             >               <Snippet id="sn.kustomer.themebuilder.conversations_all_text" />             </a>             <a                href={`/${lang}/conversations${replaceUrlParam(pageReset, 'conversationStatus', 'open')}`}               className={`status-filter ${                 openSelected                   ? "status-filter-active conversations-statusFilterSelectedBackgroundColor"                   : "conversations-statusFilterBackgroundColor"               }`}               style={{                 backgroundColor: openSelected                   ? statusFilterSelectedBackgroundColor                   : statusFilterBackgroundColor,               }}             >               <Snippet id="sn.kustomer.themebuilder.conversations_open_status_text" />             </a>             <a               href={`/${lang}/conversations${replaceUrlParam(pageReset, 'conversationStatus', 'done')}`}               className={`status-filter ${                 doneSelected                   ? "status-filter-active conversations-statusFilterSelectedBackgroundColor"                   : "conversations-statusFilterBackgroundColor"               }`}               style={{                 backgroundColor: doneSelected                   ? statusFilterSelectedBackgroundColor                   : statusFilterBackgroundColor,               }}             >               <Snippet id="sn.kustomer.themebuilder.conversations_done_status_text" />             </a>           </div>         );       }       renderConversationListItem(conversation) {         const { data } = this.props;         const conversationsPage = _.get(data,"org.manifest.pages.conversations");         const conversationsVariables = _.get(conversationsPage, "variables");         const lang = _.get(data, "org.settings.lang");         return (           <li key={conversation.id} className="conversations-list-item">             <a               href={`/${lang}/conversations/${conversation.id}`}               className="conversations-list-item-link-wrapper"             >               <div className="conversations-list-item-left">                 {conversation.name}               </div>               <div className="conversations-list-item-right">                 <div className="conversations-list-item-date">                   {data.plugins                     .moment(conversation.lastActivityAt)                     .format("M/D/YYYY")}                 </div>                 <ConversationStatus                   status={conversation.status}                   data={conversationsVariables}                 />               </div>             </a>           </li>         );       }       renderConversations() {         const { data } = this.props;         const conversations = _.get(data, "conversations.data", []);         return (           <ul className="conversations-list">             {conversations.map(this.renderConversationListItem)}           </ul>         );       }       renderPagination() {         const { data } = this.props;         const conversations = _.get(data, "conversations.data", []);         if (!conversations.length) return null;         const links = _.get(data, "conversations.links");         const currentPage = _.get(data, "conversations.meta.page");         const lang = _.get(data, "org.settings.lang");         return (           <SimplePagination             currentPage={currentPage}             links={links}             resource={`${lang}/conversations`}           />         );       }       render() {         const { data } = this.props;         const lang = _.get(data, "org.settings.lang");         return (           <main className="main-layout">             <Announcement               data={findSection(data.org.manifest, "announcement")}               template={data.template}             />             <Nav               data={findSection(data.org.manifest, "header")}               settings={org.settings}             />             <div className="conversations-container">               <ConversationBreadcrumbs view="list" lang={lang} />               <section className="container-home conversations">                 {this.renderHeader()}                 {this.renderStatusFilters()}                 {this.renderConversations()}                 {this.renderPagination()}               </section>             </div>             <ContactUs data={findSection(data.org.manifest, "contactUs")} />             <Footer data={findSection(data.org.manifest, "footer")} />           </main>         );       }     }     Conversations.defaultProps = {       data: {},       org: {},     };     return React.createElement(Conversations, {       data,       org,     });   })()} </React.Fragment>;Create new login.jsx fileSelect Add and use the following options to create the new file:Theme Target: PagePage Type: LoginCopy and paste the following code in the new file:<React.Fragment>   {(() => {     class Login extends React.PureComponent {       constructor(props) {         super(props);       }       render() {         const { data } = this.props;         return (           <main className="main-layout">             <Announcement               data={findSection(data.org.manifest, "announcement")}               template={data.template}             />             <Nav               data={findSection(data.org.manifest, "header")}               settings={org.settings}             />             <section className="container-home">               <div className="login-buttons-container">                 <div className="login-buttons-form">                   <h1 className="login-header">                     <Snippet id="sn.kustomer.themebuilder.login_welcome_text" />                   </h1>                   <LoginForm data={data} />                 </div>               </div>             </section>             <ContactUs data={findSection(data.org.manifest, "contactUs")} />             <Footer data={findSection(data.org.manifest, "footer")} />           </main>         );       }     }     Login.defaultProps = {       data: {},       org: {},     };     return React.createElement(Login, {       data,       org,     });   })()} </React.Fragment>;Create new ConversationBreadcrumbs.jsx fileSelect Add and use the following options to create the new file:Theme Target: ComponentFile Name: ConversationBreadcrumbsCopy and paste the following code in the new file:<div className="container conversations-breadcrumb-container">   <div className="conversations-breadcrumb">     <a href={`/lang/${lang}${replaceUrlParam(window.location.search, 'page', '')}`} className="conversations-breadcrumbs-link">       <Snippet id="sn.kustomer.themebuilder.conversations_company_support_text" />     </a>     <i       className="mdi mdi-chevron-right breadcrumb-chevron-right-icon"       aria-hidden="true"     />     {view === "list" ? (       <span className="conversations-breadcrumb-text">         <Snippet id="sn.kustomer.themebuilder.conversations_primary_text_value" />       </span>     ) : (       <a href={`/${lang}/conversations${window.location.search}`} className="conversations-breadcrumbs-link">         <Snippet id="sn.kustomer.themebuilder.conversations_primary_text_value" />       </a>     )}     {view === "detail" && name && (       <React.Fragment>         <i           className="mdi mdi-chevron-right breadcrumb-chevron-right-icon"           aria-hidden="true"         />         <span className="conversations-breadcrumb-text">{name}</span>       </React.Fragment>     )}   </div> </div>;Create new ConversationDetails.jsx fileSelect Add and use the following options to create the new file:Theme Target: ComponentFile Name: ConversationDetailsCopy and paste the following code in the new file:<React.Fragment>   {(() => {     class ConversationDetails extends React.PureComponent {       renderAttribute(attr, index) {         const attributeType = _.get(attr, 'attributeType');         const attribute = _.get(attr, 'attribute');         const displayName = _.get(attr, 'displayName');         let value = _.get(attr, 'value');         if (Array.isArray(value)) {           value = value.join(', ');         } else if (attributeType === 'date') {            value = plugins.moment(value).format('L LT');         } else if (attributeType === 'url' && value.startsWith('http')) {           value = <a href={value} target="_blank" rel="noopener noreferrer">{value}</a>;         }         return (           <div key={`${attribute}-${index}`} className="attribute-container">             <span className="attribute-label">{displayName}</span>             <span className="attribute-value">{value}</span>           </div>         );       }       render() {         const { className, onExpand, expandMenu, data, conversation } = this.props;         const detailsText = _.get(data, "detailsText.value");         const status = _.get(conversation, 'attributes.status');         const STANDARD_ATTRS = [           {             attribute: 'status',             attributeType: 'string',             type: 'conversation',             displayName: <Snippet id="sn.kustomer.themebuilder.status" />,             value: status === 'done' ? 'done' : 'open',           },           {             attribute: "channels",             attributeType: 'enum',             type: "conversation",             displayName: <Snippet id="sn.kustomer.themebuilder.channel" />,             value: _.get(conversation, 'attributes.channels'),           },           {             attribute: "createdAt",             attributeType: 'date',             type: "conversation",             displayName: <Snippet id="sn.kustomer.themebuilder.created_at" />,             value: _.get(conversation, 'attributes.createdAt'),           },         ];         const allAttrs = STANDARD_ATTRS.concat(_.get(conversation, 'mappedAttributes') || []);         return (           <div className={`details-container ${className}`} onClick={onExpand}>             <div className="details-header">               <h2 className="header-primaryMessage details-title conversation-detailsText">                 {eval(detailsText)}               </h2>               <i                 className={`mdi mdi-chevron-${                   expandMenu ? "up" : "down"                 } detail-expand-icon`}                 aria-hidden="true"               />             </div>             <div               className={`attributes-container ${                 expandMenu ? "open" : "close"               }`}             >               {allAttrs.map((attr, index) => {                 return this.renderAttribute(attr, index);               })}             </div>           </div>         );       }     }     ConversationDetails.defaultProps = {       conversation: {},       expandMenu: false,       onExpand: () => {},       className: "",       data: {},     };     return React.createElement(ConversationDetails, {       conversation,       expandMenu,       onExpand,       className,       data,     });   })()} </React.Fragment>;Create new ConversationStatus.jsx fileSelect Add and use the following options to create the new file:Theme Target: ComponentFile Name: ConversationStatusCopy and paste the following code in the new file:<div   className={`conversations-list-item-status conversations-${status}StatusBackgroundColor ${status}`}   style={{     backgroundColor: status === "done"       ? _.get(data, "doneStatusBackgroundColor.value")        : _.get(data,"openStatusBackgroundColor.value"),   }}   >   <span     className={`conversations-${status}StatusTextColor`}     style={{       color: status === "done" ?  _.get(data, "doneStatusTextColor.value") : _.get(data, "openStatusTextColor.value"),     }}   >     {status}   </span> </div>Create a new 401.jsx fileSelect Add and use the following options to create the new file:Theme Target: PagePage Type: 401Copy and paste the following code in the new file:<main className="main-layout">   <Announcement     data={findSection(data.org.manifest, "announcement")}     template={data.template}   />   <Nav     data={findSection(data.org.manifest, "header")}     settings={org.settings}   />   <section className="container-home">     <div>       <a href="javascript:history.back()">         <i className="icon-search mdi mdi-arrow-left fourOhFour-icon" aria-hidden="true"></i>       </a>     </div>     <div className="fourOhFour-wrap">       <h1 className="fourOhFour-header">         <Snippet id="sn.kustomer.error" /> 401       </h1>       <h2 className="fourOhFour-subheader">         <Snippet id="sn.kustomer.themebuilder.401_message" />       </h2>       <div className="fourOhFour-body">         <NotPermittedSvg className="fourOhFour-image" />         <button className="btn" onClick={() => { location.href='/' }}>           <Snippet id="sn.kustomer.themebuilder.back_to_home_button" />         </button>       </div>     </div>   </section>   <Footer data={findSection(data.org.manifest, "footer")} /> </main>Step 2: Update existing theme filesNext, update the theme files with the changes shown in this procedure.Note: If you have existing custom changes that you made to any of the below files, make sure you also port them over.Update the styles.css fileOpen the styles.css file and make the following changes:Find class .container-home and change margin: 2.5rem; to margin: 1.5rem;Find class .contactUs-primaryText and add .conversations-primaryText to the list.Find class .contactUs-secondaryText and add .conversations-secondaryText to the list.Find class .text-overflow and add word-break: break-word;.Find .four0hFour and paste the following code near .four0hFour styles:.fourOhFour-icon { font-size: 1.5rem; } .fourOhFour-image {   display: block;   margin: 3rem auto;   max-width: 100%; }Find .article-item-header-wrap and replace that code with:.article-item-header-wrap {   transition: color 0.2s ease-in-out;   font-size: 1rem;   font-weight: bold;   display: flex; } .article-item-header-wrap:hover .article-item-header-icon:before {   color: inherit !important; } .article-item-header-icon {   margin-left: 5px; }Update the util.js fileOpen the utils.js file and replace the code in it with:function findSection(manifest, name) {   if (Array.isArray(manifest)) {     return manifest.filter(function (section) {       return section.name === name;     })[0];   } else {     const section = _.get(manifest, `pages.homepage.variables[${name}]`);     return section;   } } function getAbsoluteLink(link) {   if (!link) return "";   const httpsNotFound = link.indexOf("https://") === -1;   const httpNotFound = link.indexOf("http://") === -1;   let absoluteLink = link;   if (httpsNotFound && httpNotFound) {     absoluteLink = `https://${link}`;   }   return absoluteLink; } Update the homepage.jsx fileOpen the homepage.jsx file and replace the code in it with:<React.Fragment>   {(() => {     class Homepage extends React.PureComponent {       constructor(props) {         super(props);       }       render() {         const manifest = data.org.manifest;         return (           <main className="main-layout">             <Announcement               key="announcement"               data={findSection(manifest, "announcement")}               template={data.template}             />             <Nav               key="header"               data={findSection(manifest, "header")}               settings={org.settings}             />             <SearchHeaderWithSuggestions               key="searchBar"               data={findSection(manifest, "searchBar")}               org={org}               domain={data.domain}               HeroImage={HeroImage}             />             <CategoriesSection               key="category"               data={findSection(manifest, "category")}               categories={categories}               CategoryBlock={CategoryBlock}             />             <FeaturedArticles               key="featuredArticles"               data={findSection(manifest, "featuredArticles")}               featuredArticles={featuredArticles}               ArticleItem={ArticleItem}             />             <ContactUs               key="contactUs"               data={findSection(manifest, "contactUs")}             />             <Footer key="footer" data={findSection(manifest, "footer")} />           </main>         );       }     }     return React.createElement(Homepage);   })()} </React.Fragment>;Update the nav.jsx fileOpen the nav.jsx file and replace the code in it with:<React.Fragment> {(() => { class Nav extends React.PureComponent { constructor(props) { super(props); this.mql = window.matchMedia("screen and (min-width: 768px)"); this.state = { showDesktopMenu: this.mql.matches, showMobileMenu: false, lockBody: false, showUserMenu: false, }; this.handleMediaChange = this.handleMediaChange.bind(this); this.handleOpenMobileMenu = this.handleOpenMobileMenu.bind(this); this.handleCloseMobileMenu = this.handleCloseMobileMenu.bind(this); this.renderNavListItem = this.renderNavListItem.bind(this); this.handleLockBody = this.handleLockBody.bind(this); this.handleToggleAccountMenu = this.handleToggleAccountMenu.bind(this); this.handleOnClickUserMenu = this.handleOnClickUserMenu.bind(this); this.handleClickOutside = this.handleClickOutside.bind(this); } componentDidMount() { try { // Chrome & Firefox this.mql.addEventListener( "change", _.throttle(this.handleMediaChange) ); } catch (e) { try { // Safari this.mql.addListener(_.throttle(this.handleMediaChange)); } catch (err) { console.error(err); } } document.addEventListener('mousedown', this.handleClickOutside); } componentWillUnmount() { document.removeEventListener('mousedown', this.handleClickOutside); } handleMediaChange(mediaQueryEvent) { const { showMobileMenu } = this.state; this.setState({ showDesktopMenu: mediaQueryEvent.matches }); if (mediaQueryEvent.matches && showMobileMenu) { this.handleCloseMobileMenu(); } } handleOpenMobileMenu() { document.body.classList.add("lock-body"); this.setState({ showMobileMenu: true }); } handleCloseMobileMenu() { document.body.classList.remove("lock-body"); this.setState({ showMobileMenu: false }); } handleLockBody() { this.setState( (prevState) => ({ lockBody: !prevState.lockBody }), () => { if (this.state.lockBody) { document.body.classList.add("lock-body"); } else { document.body.classList.remove("lock-body"); } } ); } handleToggleAccountMenu() { this.setState((prevState) => { return { showUserMenu: !prevState.showUserMenu, }; }); } handleOnClickUserMenu(mode) { this.handleToggleAccountMenu(); if (mode === "mobile") { this.handleLockBody(); } } handleClickOutside(event) { if (this.userMenu && !this.userMenu.contains(event.target)) { this.setState({ showUserMenu: false }); } } renderAccountList(mode) { const { settings } = this.props; const { showUserMenu } = this.state; if (!showUserMenu) return null; let listItemClassName = "nav-account-list-item"; if (mode === "mobile") { listItemClassName += " nav-list-item nav-list-item-mobile"; } const lang = _.get(settings, 'lang'); const pageReset = replaceUrlParam(window.location.search, 'page', '1'); const conversationsHref = `/${lang}/conversations${replaceUrlParam(pageReset, 'q', '')}`; return ( <ul className="nav-account-list-container" role="menu"> { _.get(settings, "customer") && ( <li className={listItemClassName}> <a href={conversationsHref} className="nav-account-link nav-account-link-conversations"> <Snippet id="sn.kustomer.themebuilder.conversations_primary_text_value" /> </a> </li> ) } <li className={listItemClassName}> <a className="nav-account-link nav-account-link-logout" href={`/logout${window.location.search}`}> <Snippet id="sn.kustomer.themebuilder.logout_text" /> </a> </li> </ul> ); } renderLoginOrUserMenu(mode) { const { settings } = this.props; const { showUserMenu } = this.state; const direction = showUserMenu ? "up" : "down"; const isPortalEnabled = _.get(settings, "portalSettings.enabled", false); const internalModeEnabled = _.get(settings, "portalSettings.internalModeEnabled", false); const customer = _.get(settings, "customer") || _.get(settings, "agent"); if (!isPortalEnabled && !internalModeEnabled) return null; if (!customer && isPortalEnabled) { return ( <div className={`nav-account-container ${mode}`}> <div className="nav-account"> <a href="/login" className="nav-account-link nav-account-link-login"> <Snippet id="sn.kustomer.themebuilder.login_text" /> </a> </div> </div> ); } if (customer) { const avatarUrl = _.get(customer, "avatarUrl"); const name = _.get(customer, "name"); const email = _.get(customer, "email"); return ( <div key={`UserMenu-${mode}`} className={`nav-account-container ${mode}`} onClick={() => this.handleOnClickUserMenu(mode)} ref={(el) => { if (mode === "desktop") { this.userMenu = el; } }} > <div className="nav-account"> {avatarUrl && ( <img src={avatarUrl} className="nav-avatar" alt="avatar-icon" /> )} <div className="nav-account-name">{name || email}</div> <i className={`mdi mdi-menu-${direction} nav-menu-${direction}-icon nav-menu-icon`} aria-hidden="true" /> </div> {this.renderAccountList(mode)} </div> ); } return null; } renderLogo() { const { settings } = this.props; const logo = _.get(settings, "logo"); return ( <div className="nav-flex"> {logo && ( <a href="/" id="top"> <img src={logo} className="header-logo" alt="Homepage" /> </a> )} </div> ); } renderMobileNav() { const { showMobileMenu } = this.state; const iconType = showMobileMenu ? "mdi-close" : "mdi-menu"; const iconOnClick = showMobileMenu ? this.handleCloseMobileMenu : this.handleOpenMobileMenu; return ( <nav className="nav"> <div className="container"> {this.renderLogo()} <div className="nav-flex"> <i className={`mdi ${iconType} nav-mobile-menu-icon`} onClick={iconOnClick} aria-hidden="true" /> {this.renderLoginOrUserMenu("mobile")} </div> </div> {showMobileMenu && this.renderNavList()} </nav> ); } renderNavListItem(item, index) { const { showMobileMenu } = this.state; const navigationLink = _.get(item, "navigationLink.value"); const navigationText = _.get(item, "navigationText.value"); const navListItemClassname = showMobileMenu ? "nav-list-item nav-list-item-mobile" : "nav-list-item"; return ( <li className={navListItemClassname} key={`navLink-${index}`} role="listitem" > <a href={getAbsoluteLink(navigationLink)} className="header-links"> {eval(navigationText)} </a> </li> ); } renderNavList() { const { data } = this.props; const { showMobileMenu } = this.state; const navigationLinks = _.get(data, "variables.navigation.value", []); const navListClassname = showMobileMenu ? "nav-list header-navigation nav-list-mobile" : "nav-list header-navigation"; return ( <ul id="header-navigation" className={navListClassname} data-header-navigation role="menu" > {navigationLinks.map(this.renderNavListItem)} <LanguageSelect className="language-select" /> {this.renderLoginOrUserMenu("desktop")} </ul> ); } renderDesktopNav() { return ( <nav className="nav"> <div className="container"> {this.renderLogo()} <div className="nav-flex">{this.renderNavList()}</div> </div> </nav> ); } render() { const { showDesktopMenu } = this.state; return showDesktopMenu ? this.renderDesktopNav() : this.renderMobileNav(); } } Nav.defaultProps = { data: {}, settings: {}, }; return React.createElement(Nav, { data, settings }); })()} </React.Fragment>;      render() {         const { showDesktopMenu } = this.state;         return showDesktopMenu           ? this.renderDesktopNav()           : this.renderMobileNav();       }     }     Nav.defaultProps = {       data: {},       settings: {},     };     return React.createElement(Nav, { data, settings });   })()} </React.Fragment>;Update the manifest.json fileOpen the manifest.json file and replace the code in the Advanced section with:{     "version": 2,     "design": {       "name": "design",       "variables": {         "font": {           "type": "section",           "name": "font",           "label": "",           "variables": {             "headingsAndButtonsFont": {               "type": "select",               "name": "headingsAndButtonsFont",               "label": "",               "enum": [                 {                   "label": "Arial",                   "value": "arial"                 },                 {                   "label": "Verdana",                   "value": "verdana"                 },                 {                   "label": "Helvetica",                   "value": "helvetica"                 },                 {                   "label": "Tahoma",                   "value": "tahoma"                 },                 {                   "label": "Times New Roman",                   "value": "times-new-roman"                 },                 {                   "label": "Georgia",                   "value": "georgia"                 },                 {                   "label": "Garamond",                   "value": "garamond"                 },                 {                   "label": "Courier New",                   "value": "courier-new"                 }               ],               "value": "Helvetica"             },             "headingBaseSizeFont": {               "type": "slider",               "name": "headingBaseSizeFont",               "label": "",               "value": "36px"             },             "bodyTextFont": {               "type": "select",               "name": "bodyTextFont",               "label": "",               "enum": [                 {                   "label": "Arial",                   "value": "arial"                 },                 {                   "label": "Verdana",                   "value": "verdana"                 },                 {                   "label": "Helvetica",                   "value": "helvetica"                 },                 {                   "label": "Tahoma",                   "value": "tahoma"                 },                 {                   "label": "Times New Roman",                   "value": "times-new-roman"                 },                 {                   "label": "Georgia",                   "value": "georgia"                 },                 {                   "label": "Garamond",                   "value": "garamond"                 },                 {                   "label": "Courier New",                   "value": "courier-new"                 }               ],               "value": "Helvetica"             },             "baseSizeFont": {               "type": "slider",               "name": "baseSizeFont",               "label": "",               "value": "16px"             }           }         },         "colors": {           "type": "section",           "name": "colors",           "label": "",           "variables": {             "font": {               "type": "group",               "name": "font",               "label": "",               "variables": {                 "headingsColor": {                   "type": "color",                   "name": "headingsColor",                   "label": "",                   "value": "#0A3355"                 },                 "bodyTextColor": {                   "type": "color",                   "name": "bodyTextColor",                   "label": "",                   "value": "#222222"                 },                 "secondaryTextColor": {                   "type": "color",                   "name": "secondaryTextColor",                   "label": "",                   "value": "#576977"                 },                 "linksColor": {                   "type": "color",                   "name": "linksColor",                   "label": "",                   "value": "#0A3355"                 },                 "linkTextHoverColor": {                   "type": "color",                   "name": "linkTextHoverColor",                   "label": "",                   "value": "#005FAD"                 }               }             },             "buttons": {               "type": "group",               "name": "buttons",               "label": "",               "variables": {                 "primaryButtonColor": {                   "type": "color",                   "name": "primaryButtonColor",                   "label": "",                   "value": "#0A3355"                 },                 "primaryButtonTextColor": {                   "type": "color",                   "name": "primaryButtonTextColor",                   "label": "",                   "value": "#ffffff"                 },                 "primaryButtonBackgroundHoverColor": {                   "type": "color",                   "name": "primaryButtonBackgroundHoverColor",                   "label": "",                   "value": "#005FAD"                 },                 "primaryButtonTextHoverColor": {                   "type": "color",                   "name": "primaryButtonTextHoverColor",                   "label": "",                   "value": "#ffffff"                 }               }             },             "page": {               "type": "group",               "name": "page",               "label": "",               "variables": {                 "primaryBackgroundPageColor": {                   "type": "color",                   "name": "primaryBackgroundPageColor",                   "label": "",                   "value": "#ffffff"                 },                 "secondaryBackgroundPageColor": {                   "type": "color",                   "name": "secondaryBackgroundPageColor",                   "label": "",                   "value": "#F9F9F9"                 },                 "borderPageColor": {                   "type": "color",                   "name": "borderPageColor",                   "label": "",                   "value": "#B2B2B2"                 }               }             }           }         }       }     },     "pages": {       "homepage": {         "type": "page",         "name": "homepage",         "variables": {           "announcement": {             "type": "section",             "name": "announcement",             "label": "[[ sn.kustomer.themebuilder.announcement_label ]]",             "enabled": true,             "variables": {               "homepageOnly": {                 "type": "boolean",                 "name": "homepageOnly",                 "label": "[[ sn.kustomer.themebuilder.announcement_show_on_home_page_label ]]",                 "value": false               },               "announcementText": {                 "type": "text",                 "name": "announcementText",                 "label": "[[ sn.kustomer.themebuilder.announcement_text_value ]]",                 "value": "Important update about our holiday hours"               },               "textColor": {                 "type": "color",                 "name": "textColor",                 "label": "[[ sn.kustomer.themebuilder.announcement_text_color_label ]]",                 "value": "#FFFFFF"               },               "backgroundColor": {                 "type": "color",                 "name": "backgroundColor",                 "label": "[[ sn.kustomer.themebuilder.announcement_background_color_label ]]",                 "value": "#E16360"               },               "ctaText": {                 "type": "string",                 "name": "ctaText",                 "label": "[[ sn.kustomer.themebuilder.cta_text_label ]]",                 "value": "<Snippet id=\"sn.kustomer.themebuilder.announcement_cta_text_value\" />"               },               "ctaTextColor": {                 "type": "color",                 "name": "ctaTextColor",                 "label": "[[ sn.kustomer.themebuilder.announcement_cta_text_color_label ]]",                 "value": "#FFFFFF"               },               "ctaLink": {                 "type": "link",                 "name": "ctaLink",                 "label": "[[ sn.kustomer.themebuilder.cta_link_label ]]",                 "value": ""               }             }           },           "header": {             "type": "section",             "name": "header",             "label": "[[ sn.kustomer.themebuilder.header_label ]]",             "link": {               "label": "[[ sn.kustomer.themebuilder.header_link_label ]]",               "description": "[[ sn.kustomer.themebuilder.header_link_description ]]",               "buttonText": "[[ sn.kustomer.themebuilder.header_link_button_text ]]",               "value": "/app/settings/kb/config"             },             "variables": {               "navigation": {                 "type": "list",                 "name": "navigation",                 "minItems": 1,                 "maxItems": 5,                 "label": "[[ sn.kustomer.themebuilder.header_navigation_label ]]",                 "buttonText": "[[ sn.kustomer.themebuilder.header_navigation_button_text ]]",                 "value": [                   {                     "navigationText": {                       "type": "string",                       "name": "navigationText",                       "label": "[[ sn.kustomer.themebuilder.navigation_text_label_1 ]]",                       "value": "test"                     },                     "navigationLink": {                       "type": "link",                       "name": "navigationLink",                       "label": "[[ sn.kustomer.themebuilder.navigation_link_label_1 ]]",                       "value": ""                     }                   },                   {                     "navigationText": {                       "type": "string",                       "name": "navigationText",                       "label": "[[ sn.kustomer.themebuilder.navigation_text_label_1 ]]",                       "value": "<Snippet id=\"sn.kustomer.themebuilder.navigation_text_value_1\" />"                     },                     "navigationLink": {                       "type": "link",                       "name": "navigationLink",                       "label": "[[ sn.kustomer.themebuilder.navigation_link_label_1 ]]",                       "value": ""                     }                   }                 ]               }             }           },           "searchBar": {             "type": "section",             "name": "searchBar",             "label": "[[ sn.kustomer.themebuilder.search_bar_label ]]",             "variables": {               "primaryMessage": {                 "type": "string",                 "name": "primaryMessage",                 "label": "[[ sn.kustomer.themebuilder.search_bar_primary_message_label ]]",                 "value": "<Snippet id=\"sn.kustomer.themebuilder.search_bar_primary_message_value\" />"               },               "secondaryMessage": {                 "type": "text",                 "name": "secondaryMessage",                 "label": "[[ sn.kustomer.themebuilder.search_bar_secondary_message_label ]]",                 "value": "Everything you need to know about Acme Corp"               },               "overlayTextColor": {                 "type": "color",                 "name": "overlayTextColor",                 "label": "[[ sn.kustomer.themebuilder.search_bar_overlay_text_color_label ]]",                 "value": "#FFFFFF"               },               "promptText": {                 "type": "string",                 "name": "promptText",                 "label": "[[ sn.kustomer.themebuilder.search_bar_prompt_text_label ]]",                 "value": "<Snippet id=\"sn.kustomer.themebuilder.search_bar_prompt_text_value\" />"               },               "heroImage": {                 "type": "file",                 "name": "heroImage",                 "label": "[[ sn.kustomer.themebuilder.search_bar_hero_image_label ]]",                 "value": "https://cdnkb.kustomerapp.com/templates/midtown/images/150x150transparent.png"               },               "overlayColor": {                 "type": "color",                 "name": "overlayColor",                 "label": "[[ sn.kustomer.themebuilder.search_bar_overlay_color_label ]]",                 "value": "#0a3355"               },               "overlayOpacity": {                 "type": "slider",                 "name": "overlayOpacity",                 "label": "[[ sn.kustomer.themebuilder.search_bar_overlay_opacity_label ]]",                 "value": 10               }             }           },           "category": {             "type": "section",             "name": "category",             "label": "[[ sn.kustomer.themebuilder.category_label ]]",             "enabled": true,             "link": {               "label": "[[ sn.kustomer.themebuilder.category_link_label ]]",               "description": "[[ sn.kustomer.themebuilder.category_link_description ]]",               "learnMoreLink": "https://help.kustomer.com/brands-and-categories-rJEANXXsB#CategoryIcon",               "buttonText": "[[ sn.kustomer.themebuilder.category_link_button_text ]]",               "value": "/app/settings/kb/categories"             },             "variables": {               "primaryText": {                 "type": "string",                 "name": "primaryText",                 "label": "[[ sn.kustomer.themebuilder.primary_text_label ]]",                 "value": "<Snippet id=\"sn.kustomer.themebuilder.category_primary_text_value\" />"               },               "secondaryText": {                 "type": "text",                 "name": "secondaryText",                 "label": "[[ sn.kustomer.themebuilder.secondary_text_label ]]",                 "value": "<Snippet id=\"sn.kustomer.themebuilder.category_secondary_text_value\" />"               }             }           },           "featuredArticles": {             "type": "section",             "name": "featuredArticles",             "label": "[[ sn.kustomer.themebuilder.featured_articles_label ]]",             "enabled": true,             "link": {               "label": "[[ sn.kustomer.themebuilder.featured_articles_link_label ]]",               "description": "[[ sn.kustomer.themebuilder.featured_articles_link_description ]]",               "learnMoreLink": "https://help.kustomer.com/featured-articles-SkWJSGJbO",               "buttonText": "[[ sn.kustomer.themebuilder.featured_articles_link_button_text ]]",               "value": "/app/settings/kb/articles"             },             "variables": {               "primaryText": {                 "type": "string",                 "name": "primaryText",                 "label": "[[ sn.kustomer.themebuilder.primary_text_label ]]",                 "value": "<Snippet id=\"sn.kustomer.themebuilder.featured_articles_label\" />"               },               "secondaryText": {                 "type": "text",                 "name": "secondaryText",                 "label": "[[ sn.kustomer.themebuilder.secondary_text_label ]]",                 "value": "<Snippet id=\"sn.kustomer.themebuilder.featured_articles_secondary_text_value\" />"               }             }           },           "contactUs": {             "type": "section",             "name": "contactUs",             "label": "[[ sn.kustomer.themebuilder.contact_us_label ]]",             "enabled": true,             "variables": {               "primaryText": {                 "type": "string",                 "name": "primaryText",                 "label": "[[ sn.kustomer.themebuilder.primary_text_label ]]",                 "value": "<Snippet id=\"sn.kustomer.themebuilder.contact_us_primary_text_label\" />"               },               "secondaryText": {                 "type": "text",                 "name": "secondaryText",                 "label": "[[ sn.kustomer.themebuilder.secondary_text_label ]]",                 "value": "<Snippet id=\"sn.kustomer.themebuilder.contact_us_secondary_text_value\" />"               },               "ctaText": {                 "type": "string",                 "name": "ctaText",                 "label": "[[ sn.kustomer.themebuilder.cta_text_label ]]",                 "value": "<Snippet id=\"sn.kustomer.themebuilder.contact_us_label\" />"               },               "ctaLink": {                 "type": "link",                 "name": "ctaLink",                 "label": "[[ sn.kustomer.themebuilder.cta_link_label ]]",                 "value": ""               }             }           },           "footer": {             "type": "section",             "name": "footer",             "label": "[[ sn.kustomer.themebuilder.footer_label ]]",             "variables": {               "navigation": {                 "type": "list",                 "name": "navigation",                 "minItems": 1,                 "maxItems": 5,                 "collapsible": true,                 "label": "[[ sn.kustomer.themebuilder.footer_navigation_label ]]",                 "buttonText": "[[ sn.kustomer.themebuilder.footer_navigation_button_text ]]",                 "value": [                   {                     "navigationText": {                       "type": "string",                       "name": "navigationText",                       "label": "[[ sn.kustomer.themebuilder.navigation_text_label_1 ]]",                       "value": "<Snippet id=\"sn.kustomer.themebuilder.navigation_text_value_1\" />"                     },                     "navigationLink": {                       "type": "link",                       "name": "navigationLink",                       "label": "[[ sn.kustomer.themebuilder.navigation_link_label_1 ]]",                       "value": ""                     }                   }                 ]               },               "social": {                 "type": "list",                 "name": "social",                 "minItems": 1,                 "maxItems": 5,                 "collapsible": true,                 "label": "[[ sn.kustomer.themebuilder.footer_social_label ]]",                 "buttonText": "[[ sn.kustomer.themebuilder.footer_social_button_text ]]",                 "value": [                   {                     "socialIcon": {                       "type": "icon",                       "name": "socialIcon",                       "label": "[[ sn.kustomer.themebuilder.footer_social_icon_label ]]",                       "enum": [                         {                           "label": "facebook",                           "value": "facebook"                         },                         {                           "label": "instagram",                           "value": "instagram"                         },                         {                           "label": "twitter",                           "value": "twitter"                         },                         {                           "label": "pinterest",                           "value": "pinterest"                         },                         {                           "label": "linkedin",                           "value": "linkedin"                         }                       ],                       "value": "facebook"                     },                     "socialLink": {                       "type": "link",                       "name": "socialLink",                       "label": "[[ sn.kustomer.themebuilder.footer_social_link_label ]]",                       "value": ""                     }                   }                 ]               },               "copyrightInfo": {                 "type": "string",                 "name": "copyrightInfo",                 "label": "[[ sn.kustomer.themebuilder.footer_copyright_label ]]",                 "value": "All rights reserved 2020"               },               "footerPrimaryTextColor": {                 "type": "color",                 "name": "footerPrimaryTextColor",                 "label": "[[ sn.kustomer.themebuilder.footer_primary_text_color_label ]]",                 "value": "#FFFFFF"               },               "footerSecondaryTextColor": {                 "type": "color",                 "name": "footerSecondaryTextColor",                 "label": "[[ sn.kustomer.themebuilder.footer_secondary_text_color_label ]]",                 "value": "#939FA8"               },               "footerBackgroundColor": {                 "type": "color",                 "name": "footerBackgroundColor",                 "label": "[[ sn.kustomer.themebuilder.footer_background_color_label ]]",                 "value": "#0a3355"               }             }           }         }       },       "conversations": {         "type": "page",         "name": "conversations",         "label": "[[ sn.kustomer.themebuilder.conversations_label ]]",         "variables": {           "primaryText": {             "type": "string",             "name": "primaryText",             "label": "[[ sn.kustomer.themebuilder.primary_text_label ]]",             "value": "<Snippet id=\"sn.kustomer.themebuilder.conversations_primary_text_value\" />"           },           "secondaryText": {             "type": "text",             "name": "secondaryText",             "label": "[[ sn.kustomer.themebuilder.secondary_text_label ]]",             "value": "<Snippet id=\"sn.kustomer.themebuilder.conversations_secondary_text_value\" />"           },           "openStatusTextColor": {             "type": "color",             "name": "openStatusTextColor",             "label": "[[ sn.kustomer.themebuilder.conversations_open_status_text_color_label ]]",             "value": "#15CC70"           },           "openStatusBackgroundColor": {             "type": "color",             "name": "openStatusBackgroundColor",             "label": "[[ sn.kustomer.themebuilder.conversations_open_status_background_color_label ]]",             "value": "#e8faf1"           },           "doneStatusTextColor": {             "type": "color",             "name": "doneStatusTextColor",             "label": "[[ sn.kustomer.themebuilder.conversations_done_status_text_color_label ]]",             "value": "#767676"           },           "doneStatusBackgroundColor": {             "type": "color",             "name": "doneStatusBackgroundColor",             "label": "[[ sn.kustomer.themebuilder.conversations_done_status_background_color_label ]]",             "value": "#f1f1f1"           },           "statusFilterBackgroundColor": {             "type": "color",             "name": "statusFilterBackgroundColor",             "label": "[[ sn.kustomer.themebuilder.conversations_status_filter_background_color_label ]]",             "value": "#FFFFFF"           },           "statusFilterSelectedBackgroundColor": {             "type": "color",             "name": "statusFilterSelectedBackgroundColor",             "label": "[[ sn.kustomer.themebuilder.conversations_status_filter_selected_background_color_label ]]",             "value": "#E3E3E3"           }         }       },       "conversation": {         "type": "page",         "name": "conversation",         "label": "[[ sn.kustomer.themebuilder.conversation_label ]]",         "variables": {           "detailsText": {             "type": "text",             "name": "detailsText",             "label": "[[ sn.kustomer.themebuilder.conversation_details_text_label ]]",             "value": "<Snippet id=\"sn.kustomer.themebuilder.conversation_details_text_value\" />"           },           "sendButtonText": {             "type": "text",             "name": "sendButtonText",             "label": "[[ sn.kustomer.themebuilder.conversation_send_button_text_label ]]",             "value": "<Snippet id=\"sn.kustomer.themebuilder.conversation_send_button_text_value\" />"           },           "messageFailedText": {             "type": "text",             "name": "messageFailedText",             "label": "[[ sn.kustomer.themebuilder.conversation_message_failed_text_label ]]",             "value": "<Snippet id=\"sn.kustomer.themebuilder.message_error_text\" />"           },           "messagePendingText": {             "type": "text",             "name": "messagePendingText",             "label": "[[ sn.kustomer.themebuilder.conversation_message_pending_text_label ]]",             "value": "<Snippet id=\"sn.kustomer.themebuilder.message_pending_text\" />"           },           "replyBoxPlaceholderText": {             "type": "text",             "name": "replyBoxPlaceholderText",             "label": "[[ sn.kustomer.themebuilder.conversation_details_reply_placeholder_text_label ]]",             "value": "<Snippet id=\"sn.kustomer.themebuilder.conversation_details_reply_placeholder_text_value\" />"           },           "userMessageBackgroundColor": {             "type": "color",             "name": "userMessageBackgroundColor",             "label": "[[ sn.kustomer.themebuilder.conversation_user_message_background_color_label ]]",             "value": "#F4F3F8"           },           "userMessageTextColor": {             "type": "color",             "name": "userMessageTextColor",             "label": "[[ sn.kustomer.themebuilder.conversation_user_message_text_color_label ]]",             "value": "#000000"           },           "agentMessageBackgroundColor": {             "type": "color",             "name": "agentMessageBackgroundColor",             "label": "[[ sn.kustomer.themebuilder.conversation_agent_message_background_color_label ]]",             "value": "#FFF1F0"           },           "agentMessageTextColor": {             "type": "color",             "name": "agentMessageTextColor",             "label": "[[ sn.kustomer.themebuilder.conversation_agent_message_text_color_label ]]",             "value": "#000000"           }         }       }     }   }Note: Please ensure you’ve copied over values from your existing manifest to the updated manifest data structure. For example, if the existing value of your announcementText is 50% off! , make sure to copy this value over to the updated data structure:"announcementText": {     "name": "announcementText",     "type": "text",     "label": "[[ sn.kustomer.themebuilder.announcement_text_value ]]",     "value": "50% off! " }, Update the ArticleItem.jsx fileOpen the ArticleItem.jsx file, find <h2 className="article-item-header">{title}</h2> and replace that code with:<h2 className="article-item-header">   {title}   {scope === 'internal' && (     <Tooltip content={<Snippet id="sn.kustomer.themebuilder.internal_article_icon_tooltip" />}>       <i className="mdi mdi-lock article-item-header-icon" aria-hidden="true" />     </Tooltip>   )} </h2>Update the SearchItem.jsx fileOpen the SearchItem.jsx file and make the following changes:Add scope to the default properties listed under articleId on lines 97 and 111Find <h2 className="article-item-header bold">{title}</h2> and replace that code with:<h2 className="article-item-header bold">   {title}    {scope === 'internal' && (       <Tooltip content={<Snippet id="sn.kustomer.themebuilder.internal_article_icon_tooltip" />}>         <i className="mdi mdi-lock article-item-header-icon" aria-hidden="true" />       </Tooltip>     )} </h2>Update the article.jsx fileOpen the article.jsx file, find <h2 className="article-title">{article.title}</h2> and replace that code with:<h2 className="article-title">   {article.title}   {article.scope === 'internal' && (     <Tooltip content={<Snippet id="sn.kustomer.themebuilder.internal_article_icon_tooltip" />}>       <i className="mdi mdi-lock article-item-header-icon" aria-hidden="true" />     </Tooltip>   )} </h2>You now have access to the Portal.
  • Add Contentsquare tracking to your knowledge base

    Contentsquare is a digital experience analytics software that allows users to track customer activity on specific websites. These insights can be used to see how many people visit your knowledge base, which articles get the most traffic, and which articles could use a little work. Contentsquare also allows users to visualize how customers interact with different elements on their site to better understand the customer journey.This article explains how to implement Contentsquare's tracking tag in your knowledge base.Who can access this feature?User typesContent administrators can access the Themes page.In this articlePrerequisitesAdd tracking tag to your themePrerequisitesYou must have access to a Contentsquare account before this can be set up in your knowledge base.You'll also need the Contentsquare Tag ID that was provided when setting up your account.Add tracking tag to your themeGo to Settings> Knowledge Base > Themes.Select the theme your knowledge base is using. If the chosen theme is using the visual editor, open the More Options menu and select Go to Code Editor.Select Create New Draft.Select Add to create a new file. Give it the following properties:Theme Target: GlobalFile Name: ContentsquareField Type: JSEnter the following code in the editor:window.addEventListener('DOMContentLoaded', (event) => { const mt = document.createElement('script'); mt.async = true; mt.src = "//t.contentsquare.net/uxa/<YOUR_TAG_ID>.js"; mt.type = "text/javascript" document.getElementsByTagName('head')[0].appendChild(mt); });In the above code, replace <YOUR_TAG_ID> with the Contentsquare Tag ID.Select Save and PublishOnce this change has been implemented, data should begin flowing into Contentsquare within three hours.
Powered by Kustomer