CINXE.COM

Office Manager

<!doctype html> <html lang="en" class="newLayout "> <!-- The AppSheet website and AppSheet apps make use of open source software subject to the licenses and copyright notices at https://www.appsheet.com/oss/notices --> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="apple-itunes-app" content="app-id=732548900"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA==" type="text/javascript"> if (self.trustedTypes && self.trustedTypes.createPolicy && !self.trustedTypes.defaultPolicy) { const escapeScriptPolicy = trustedTypes.createPolicy("default", { createHTML: (string) => { if (string === "&#8203;" || string === "" || string === "&") { return string; } else { return null; }; } }); } </script> <title>Office Manager</title> <meta name="description" content="Manage rooms, desks and other office equipment with flexible cleaning and reservation schedules, alert workflows, and notifications for different roles."> <meta name="author" content="AppSheet"/> <meta name="google-site-verification" content="97jAB4aUdRiLxKkCyBUhJnpDEWTQAJ6tOzUKFwE1p8w"/> <meta name="google-site-verification" content="qPkt9TxKqz8fFiALwco4ddvCCsGNrgRwTuE1aCBxG1k"/> <meta property="og:title" content="AppSheet : mobile apps from spreadsheets : Template: Property Management for the Business Services industry"/> <meta property="og:image" content=""/> <meta property="og:site_name" content="AppSheet"/> <link rel="shortcut icon" href="/Content/img/Favicon-Material-Rebrand.png" sizes="16x16"/> <link rel="shortcut icon" href="/Content/img/Favicon-Material-Rebrand.png" sizes="32X32"/> <!-- Reskinned stylesheet --> <link href="https://www.appsheet.com/assets/35d55fc0eedffd6aea08586d705d64dc5935fcb4ef5b7c3a70d71d74c5f09e5d.css" rel="stylesheet" type="text/css" media="screen, print" /> <!-- Google Fonts --> <link href="https://fonts.googleapis.com/css?family=Open+Sans:400,400i,700,700i" rel="stylesheet"> <!-- Navbar --> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA==" src="/Content/scripts/web/navbar.js"></script> <link href="https://www.appsheet.com/assets/df2824982268df521a89a3a59f2327b4b42289a68796a4d7f3f4d6caa80e907c.css" rel="stylesheet" type="text/css" media="screen, print" /> <!------------------------ TRACKING ------------------------> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA==" type="text/javascript"> /** * This is used by this page and MarketingRedirect.cshtml - moved above ShouldLogAnalytics * since this returns false for test accounts and we have an integration test */ function getQueryStrings() { var assoc = {}; var decode = function (s) { return decodeURIComponent(s.replace(/\+/g, " ")); }; var queryString = location.search.substring(1); var keyValues = queryString.split('&'); for (var i in keyValues) { var key = keyValues[i].split('='); if (key.length > 1) { assoc[decode(key[0])] = decode(key[1]); } } return assoc; } </script> <!-- Google tag (gtag.js) --> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA=="> window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); function glueCookieNotificationBarLoaded() { (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'}); var firstScript=d.getElementsByTagName(s)[0], dl=l!='dataLayer'?'&l='+l:''; var script = d.createElement(s); script.async = true; var url = 'https://www.googletagmanager.com/gtm.js?id='+i+dl; if (self.trustedTypes && self.trustedTypes.createPolicy) { var policy = self.trustedTypes.createPolicy('gtm-load-fix', { createScriptURL: function(_ignored) { return url; } }); script.src = policy.createScriptURL('_ignored'); } else { script.src = url; } firstScript.parentNode.insertBefore(script, firstScript); })(window,document,'script','dataLayer','GTM-MXWLX3PF'); } </script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/090510102d9f686cce5db049efd92db035e66231ef0381147401ce867b7c9e02.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/2e12937f9cad1713615022f1dac0f3ece070d2bd2c1b4869adb27283343662b3.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/3599ce53934853daad86acf51f3d077ae4175583366a41f05bab34e720a31163.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/de6cdd38c56716d906231036300becd46a35b7fc5f40d55429be80c91a935798.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/6f7fa028e348569b4380e330a8a63cf07687cc3ae0c7343ff09430158c589f08.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/caf839b139efcfe921023997f61904bb2dd5453125d71aebb7cb5df7ae70cedd.js" ></script> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA==" type="text/javascript"> /** * Checks whether all of the UTM fields are empty in local storage. */ function hasNoSetUtmFields() { return ( window.localStorage.getItem('JeeneeNewUserCampaign') == null && window.localStorage.getItem('JeeneeNewUserSource') == null && window.localStorage.getItem('JeeneeNewUserMedium') == null && window.localStorage.getItem('JeeneeNewUserContent') == null && window.localStorage.getItem('JeeneeNewUserTerm') == null ); } /** * Checks for UTM fields in URL params. */ function hasUtmFieldsUrlparams(queryParams) { return ( queryParams.hasOwnProperty('utm_campaign') || queryParams.hasOwnProperty('utm_source') || queryParams.hasOwnProperty('utm_medium') || queryParams.hasOwnProperty('utm_content') || queryParams.hasOwnProperty('utm_term') ); } /** * Sets UTM fields to direct/default values in local storage * when none are passed in via the URL upon user's first visit. * utm_content and utm_term are left undefined for direct and organic traffic. */ function setDefaultUtmPropertiesOnUserLocalStorage() { window.localStorage.setItem('JeeneeNewUserCampaign', 'direct'); window.localStorage.setItem('JeeneeNewUserSource', 'direct'); window.localStorage.setItem('JeeneeNewUserMedium', 'none'); } /** * Sets UTM values in local storage. Only process UTM parameters within the URL when * all of UTM fields in local storage are undefined. This ensures first touch attribution. * utm_campaign, utm_source, and utm_medium are required fields for every campaign, CTA, etc. * Seeing any of these fields as undefined is an indication that there are invalid UTM parameters in the URL. * utm_content and utm_term are optional. */ function setUTMPropertiesOnUserLocalStorage() { if (hasNoSetUtmFields()) { var queryParams = getQueryStrings(); if (hasUtmFieldsUrlparams(queryParams)) { if (queryParams['utm_campaign'] != null) { window.localStorage.setItem('JeeneeNewUserCampaign', queryParams['utm_campaign']); } if (queryParams['utm_source'] != null) { window.localStorage.setItem('JeeneeNewUserSource', queryParams['utm_source']); } if (queryParams['utm_medium'] != null) { window.localStorage.setItem('JeeneeNewUserMedium', queryParams['utm_medium']); } if (queryParams['utm_content'] != null) { window.localStorage.setItem('JeeneeNewUserContent', queryParams['utm_content']); } if (queryParams['utm_term'] != null) { window.localStorage.setItem('JeeneeNewUserTerm', queryParams['utm_term']); } } else { setDefaultUtmPropertiesOnUserLocalStorage(); } } } function TrackSimpleConcordEvent(eventName) { if (window.Concord && typeof window.Concord.TrackSimpleEvent === 'function') { window.Concord.TrackSimpleEvent(eventName); } } setUTMPropertiesOnUserLocalStorage(); /* All DefunctEventCalled events are deprecated, do not use */ window.Track = function (event, properties, callback) { TrackSimpleConcordEvent('Defunct Event Called'); } window.TrackSuperProp = function (properties) { TrackSimpleConcordEvent('Defunct Event Called'); } window.TrackSuperPropOnce = function (properties) { TrackSimpleConcordEvent('Defunct Event Called'); } window.TrackPeopleProp = function (properties) { TrackSimpleConcordEvent('Defunct Event Called'); } window.TrackTabClick = function (pageName, tabName, properties) { TrackSimpleConcordEvent('Defunct Event Called'); } window.TrackActionClick = function (pageName, buttonName, properties) { // Need to create an event in EventName for each combo here otherwise it's not very useful // to just know that a user clicked on a button but not what it was TrackSimpleConcordEvent('Button Click ' + pageName + ' ' + buttonName); } // Use this function to track user link clicks. You need a special function for this, since // the browser reloads a new page when the user clicks a link. window.TrackLinks = function (query, eventName, properties) { TrackSimpleConcordEvent('Defunct Event Called'); } </script> <!------------------------ END TRACKING ------------------------> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preload" href="https://fonts.googleapis.com/css2?family=Google+Sans+Text:wght@400&family=Google+Sans:wght@500&display=swap" as="style"> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Google+Sans+Text:wght@400&family=Google+Sans:wght@500&display=swap"> <link rel="stylesheet" href="/Content/css/cookienotificationbar.min.css"> </head> <body id="sample-app" class="gsuite-root"> <div class="bodyWrapper"> <!-- Hidden logo to show up on links to page --> <img src="/Content/img/Favicon-Material-Rebrand.png" alt="" class="hidden" style="display: none;"/> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA=="> window.VerifiedAjaxPostToken = 'dummy:dummy'; var verifiedAjaxPost = function (options, errorOptions) { if (!options.headers) { options.headers = {} } options.method = 'POST' errorOptions = errorOptions || {}; return $.ajax(options).fail((jqXHR, textStatus, errorThrown) => { if ( !errorOptions.disableErrorReporting && window.ErrorHandling && typeof window.ErrorHandling.ReportHttpError === 'function' ) { window.ErrorHandling.ReportHttpError('post', options.url, jqXHR.status, { error: errorOptions.error, errorThrown, textStatus, responseText: jqXHR.responseText, }); } }); } window.verifiedAjaxPost = verifiedAjaxPost var reloadPage = function (nonce) { // if there is already a nonce=, remove it var currentSearch = window.location.search var currentSearchSplit = currentSearch.split("&nonce=") if (currentSearchSplit.length > 1) { currentSearchSplit[1] = "" currentSearch = currentSearchSplit.join("") } window.location.search = currentSearch + "&nonce=" + (nonce ? nonce : Math.random()) } // GLOBALS window.DEBUG_MODE = false window.ALLOW_DEBUG_LOGGING = false window.getUserId = function () { return '-1' } window.getUserRole = function() { return 'Member'; } window.getUserCorporatePlan = function() { return 0; } window.getIsCoreAdmin = function () { return false; } window.hasRolloutByUserId = (function () { const rollouts = new Set( ["AdminConsoleAppTransfer","AdminConsoleCoreAdmin","AntlrExpressionAssert","AppSheetAdminConsole","AutoResolution","BannerForDeadlineReminderOfFirebaseBrandedAppsMigration","CurryInitialValueFix","DefaultDesktopUI","DontConstructRemoteImageUrls","EnableQuickSyncForASDBApps","EnableRtlForReactRoot","EnableUserFeedback","FirebaseHttpV1MigrationBrandedApps","FixDeletePersistence","FixLabelAttributeCache","FixTouchDoubleClick","FlickerFix","GeminiRebrand","GoogleFormsEvent","ImagesUsePartialDimensions","ImproveEditorOnboarding","InContextActionsAndColumns","InContextSlices","InitializeAppInDesktop","MapViewStackToControlMemo","NewFakeIntercom","NoClearBeforeWrite","OverrideBackNavigation","ParseAppUrlHash","PreserveInitialValuesInTransaction","ReactRootValidationErrors","RefactorOverlayActions","RequestCompression","RevampOnboardingStyles","SerializeFilterExprInSetCurrentUiState","SetSourceQualifierIdFrontend","ShowLiveLastProcessedDate","ShowProminentTableScopeActions","ShowTooltipsForActionsInCardView","SmallActionsImprovements","StaleForm","StaticTaskTableName","UpdateEmulatorExpression","UpdateValidityEnums","UseAppDefRoute","UseLogoutEndpointInApp","UseNewBootStrapBundle","UseNewLocalizedWireValueFallback","UseParentFilters","UseRecursiveFormattingRule","UseTypeDisplayInDesktopUIDrilldownTree","WidgetHelperJSS"] ); return function (rolloutName) { return rollouts.has(rolloutName); } })(); </script> <!-- Messages when creating or cloning apps --> <div id="cloningClickEater"> <div id="waitingClone"> <div class="loader">Loading...</div> <p>We're setting up your new app...</p> </div> <div id="errorClick"> <div id="errorModal" class="modal fade modal-errors-new"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close AlwaysEditable" data-dismiss="modal"> <i class="material">close</i> </button> <h5 class="modal-title-new">Error Creating App</h5> </div> <div id="modalErrorText" class="modal-body-new"></div> <div class="modal-more-errors-holder"> <div> <div id="moreErrorsAvailableText"></div> <button type="button" id="seeMoreArrow"> <i class='fa fa-chevron-down'></i> </button> <button type="button" id="seeLessArrow"> <i class='fa fa-chevron-up'></i> </button> </div> <div id="moreErrorsText" class="modal-more-errors"></div> </div> <div class="modal-footer"> <button id="ackErrorButton" class="btn btn-default AlwaysEditable" data-dismiss="modal">OK</button> <button id="templatesButton" class="btn btn-default AlwaysEditable" data-dismiss="modal">Start with a template</button> </div> </div> </div> </div> </div> </div> <header class="topNav"> <div class="primaryNav"> <div class="logo"> <a href="/"> <img class="material-rebrand-logo" src=https://www.appsheet.com/Content/img/material/appsheet_rebrand_logo.svg /> <span class="logo-text"> AppSheet </span> <span class="subscriptionType"></span> </a> </div> <nav> <div class="navicon"> <i class="material clickable">arrow_drop_down</i> </div> <ul class="non-mobile"> <li class="createanapp "> <a href="https://about.appsheet.com/how-to-create-an-app" data-label="createanapp" rel="" target=""> <span> How to create an app </span> </a> </li> <li class="pricing "> <a href="https://about.appsheet.com/pricing" data-label="pricing" rel="" target=""> <span> Pricing </span> </a> </li> <li class="apps "> <a href="https://www.appsheet.com/templates" data-label="apps" rel="" target=""> <span> Templates </span> </a> </li> <li class="blog "> <a href="https://cloud.google.com/blog/products/no-code-development" data-label="blog" rel="" target=""> <span> Blog </span> </a> </li> <li class="login "> <a href="https://www.appsheet.com/account/login" data-label="login" rel="" target=""> <span> Sign in </span> </a> </li> <li class="signup "> <a href="https://www.appsheet.com/account/login" data-label="signup" rel="" target=""> <span> Get started </span> </a> </li> </ul> <ul class="mobile"> <li class="createanapp "> <a href="https://about.appsheet.com/how-to-create-an-app" data-label="createanapp" rel="" target=""> <span> How to create an app </span> </a> </li> <li class="pricing "> <a href="https://about.appsheet.com/pricing" data-label="pricing" rel="" target=""> <span> Pricing </span> </a> </li> <li class="apps "> <a href="https://www.appsheet.com/templates" data-label="apps" rel="" target=""> <span> Templates </span> </a> </li> <li class="blog "> <a href="https://cloud.google.com/blog/products/no-code-development" data-label="blog" rel="" target=""> <span> Blog </span> </a> </li> <li class="login "> <a href="https://www.appsheet.com/account/login" data-label="login" rel="" target=""> <span> Sign in </span> </a> </li> <li class="signup "> <a href="https://www.appsheet.com/account/login" data-label="signup" rel="" target=""> <span> Get started </span> </a> </li> </ul> </nav> </div> </header> <main> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA==" type="text/javascript"> if (self.ReportingObserver) { const observer = new ReportingObserver((reports) => { reports.forEach((report) => { if (report.body.blockedURL === 'trusted-types-sink') { const ttMsg = "TT Error: " + report.body.sample; const data = { url: report.body.documentURL, msg: ttMsg, errorContext: report.body.sourceFile, line: report.body.lineNumber, }; const headers = { 'Content-Type': 'application/json', }; const blob = new Blob([JSON.stringify(data)], headers); navigator.sendBeacon('/manage/JavaScriptError', blob); } }); }, { types: ["csp-violation"], }); // Start observing errors observer.observe(); } </script> <div class="app-main"> <div class="app-header"> <div class="sample-app-header">Template</div> <h1 class="app-name">Office Manager</h1> <h2 class="app-description">Organize and track office resources during back to work.</h2> <div class="actions"> <a class="CloneButton button icon-button-outline" data-appname="OfficeManager3-1224517" data-shortname="Office Manager" data-owner="1334275" data-logo="https://storage.googleapis.com/cloud-icons/vector/building_general.png" data-storyurl="https://www.appsheet.com/templates/Organize-and-track-office-resources-during-back-to-work?appGuidString=5486a6ee-6293-4dbc-b24f-d51413fe1be6" target="_blank"> Copy and Customize </a> <a href="/template/showdef?appId=OfficeManager3-1224517" class="DetailsButton button icon-button-outline" target="_blank"> Look under the hood </a> </div> <div class="app-info"> <div class="categories"> <div class="industry"> <span>Industry:</span> <span>Business Services</span> </div> <div class="department"> <span>Function:</span> <span>Property Management</span> </div> <div class="owner-link"> <a class="link" href="/portfolio/1334275">Owner Portfolio</a> </div> </div> <div class="purpose"> <span>Manage rooms, desks and other office equipment with flexible cleaning and reservation schedules, alert workflows, and notifications for different roles.</span> </div> </div> </div> <div class="app-preview"> <div class="phone-frame"> <div id="IFrameDiv"> <div id="waiting"> <div class="loader">Loading...</div> </div> <iframe id="SimulatorIFrame" frameborder="0"></iframe> </div> </div> <div class="see-also"> Also see: <a href="/template/mobilepreview?appId=5486a6ee-6293-4dbc-b24f-d51413fe1be6" class="link" target="_blank" rel="noopener"> tablet mode &amp; fullscreen mode </a> </div> </div> </div> <div class="app-more" id="dashboard"> <ul id="UL" class="nav nav-tabs"> <li class="RootSection" id="SectionsampleHow"><a class="SectionTitle" href="#sampleHow" data-toggle="tab"><span style="min-width:50px;display:inline-block">Details</span></a></li> <li class="RootSection" id="SectionsampleData"><a class="SectionTitle" href="#sampleData" data-toggle="tab"><span style="min-width:50px;display:inline-block">Data</span></a></li> <li class="RootSection" id="SectionsampleSpec"><a class="SectionTitle" href="#sampleSpec" data-toggle="tab"><span style="min-width:50px;display:inline-block">Spec</span></a></li> </ul> <div class="tab-content"> <div id="sampleHow" class="tab-pane tab-pane-padding-top SubSection active"> <div class="app-design" style="max-width:none"> <h2>How we built this app</h2> <div class="app-design-text"> <span>This app uses Google Sheets as a simple backend, with images being stored in Google Drive. You can change though the data sources to any supported data source in AppSheet. </span> </div> </div> </div> <div id="sampleData" class="tab-pane tab-pane-padding-top SubSection active"> <div style="max-width:none;margin-bottom:10px;"> This is a preview of the data sets used in the app. </div> <div class="app-details" style="max-width:none"> <div class="app-data"> <h2 style="margin:0px 5px 5px">Data</h2> <iframe style="border:1px solid #eee" src="https://docs.google.com/spreadsheets/d/1akcNbhuGAACzm4IVgFKqK-0-BrpOPaHSrEIHnc9S4pw/pubhtml?widget=true&amp;headers=false"></iframe> </div> </div> </div> <div id="sampleSpec" class="tab-pane tab-pane-padding-top SubSection active"> <div style="max-width:none;margin-bottom:10px;"> This is a high-level model of both the data entities and the UI elements in the app. </div> <div id="spec" data-appid="5486a6ee-6293-4dbc-b24f-d51413fe1be6" data-appversion="1.000047" style="max-width:none"> <div class="loader">Loading...</div> </div> </div> </div> </div> <div class="modal" id="deleteAccountModal"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button class="AlwaysEditable cancel" data-dismiss="modal">X</button> <h5 class="modal-title"> Delete my account </h5> </div> <form method="post" action="/account/delete"> <input type="hidden" name="id" value="-1"> <div class="modal-body"> <p>We're sorry to see you go! Why are you leaving?</p> <p> <fieldset> <div> <input name="deleteAccountReason" type="radio" value="too hard" id="too-hard" style="width:auto;margin-right:10px"/><label for="too-hard" style="display:inline">It's too hard to figure out</label> </div> <div> <input name="deleteAccountReason" type="radio" value="unmet needs" id="unmet-needs" style="width:auto;margin-right:10px"/><label for="unmet-needs" style="display:inline">It doesn't do what I need it to do</label> </div> <div> <input name="deleteAccountReason" type="radio" value="just browsing" id="just-browsing" style="width:auto;margin-right:10px"/><label for="just-browsing" style="display:inline">I was just browsing</label> </div> <div> <input name="deleteAccountReason" type="radio" value="other" id="other" style="width:auto;margin-right:10px"/><label for="other" style="display:inline">Other</label> </div> </fieldset> </p> <p> <label for="delete-account-comment">Please tell us what we should improve<textarea rows="4" name="deleteAccountComment" id="delete-account-comment"></textarea></label> </p> <p> <a href="https://support.google.com/appsheet?p=reset-appsheet" class="link clear-devices-help-link"> Deletion of this account will not delete the apps or app data already downloaded to app users' devices. </a> </p> </div> <div class="modal-footer" style="overflow:auto"> <button type="submit">Delete my account</button> </div> </form> </div> </div> </div> </main> <section class="start-for-free-cta"> <a class="button icon-button-filled button-rectangle" href="/home/start">Start for free</a> </section> <footer class="bottomNavCompact"> <nav> <ul> <li> <div class="logo"> <a href="https://www.google.com" title="Google" target="_blank" rel="noopener noreferrer"> Google </a> </div> </li> <li> <div> <a href="https://about.google/intl/en/?utm_source=about.appsheet.com&utm_medium=referral&utm_campaign=appsheet-footer-en " title="About Google" target="_blank" rel="noopener noreferrer"> About Google </a> </div> </li> <li> <div> <a href="https://about.google/products/" title="Google products" target="_blank" rel="noopener noreferrer"> Google products </a> </div> </li> <li> <div> <a href="https://policies.google.com/privacy" title="Privacy" target="_blank" rel="noopener noreferrer"> Privacy </a> </div> </li> <li> <div> <a href="/Home/Terms" title="Terms" target="_blank" rel="noopener noreferrer"> Terms </a> </div> </li> <li> <div> <a aria-hidden="true" class="glue-footer__link glue-cookie-notification-bar-control" role="button" tabindex="0"> Cookies management controls </a> </div> </li> </ul> </nav> </footer> <!-- SCRIPTS --> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA==" src="/Scripts/jquery-3.6.2.min.js"></script> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA==" src="/Scripts/bundles/valjquery.js"></script> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA==" src="/Scripts/jquery.unobtrusive-ajax.min.js"></script> <!-- Various utilities (modals, tooltips, etc.) --> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA==" src="/Content/scripts/web/utilities.js?update=&#x27;20241122&#x27;.1"></script> <!-- This partial checks the account status every hour using accountStartup.js --> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA==" src="/Content/scripts/web/accountStartup.js"> </script> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA==" type="text/javascript"> $(function () { var shouldCheck = false; if (shouldCheck) { var oneHour = 60 * 60 * 1000; initCheckAccountStatus(oneHour); } }); </script> <!-- NEW APP MODAL --> <div class="modal fade" id="new-app-modal" tabindex="-1" role="dialog" aria-labelledby="new-app-modal-label" aria-hidden="true" data-backdrop="static"> <div class="modal-dialog"> <div class="modal-content thinScrollbar"> <div class="modal-header"> <button class="cancel" data-dismiss="modal" id="create-new-app-modal-close"> <i class="material">close</i> </button> <h5 class="modal-title" id="new-app-modal-label"> <span>Create a new app</span> </h5> </div> <div class="modal-body"> <div class="step-1"> <button type="button" id="new-app-start-with-data"> <img src="/Content/web/img/app-gallery/new-app-from-data.svg" alt=""/> <span>Start with your own data</span> </button> <button type="button" id="new-app-start-with-sample"> <img src="/Content/web/img/app-gallery/new-app-from-sample.svg" alt=""/> <span>Start with a template</span> </button> </div> <!-- COPY SAMPLE --> <div class="step-2 step-2-sample"> <div class="step-2-header">Select a template to copy</div> <div class="new-app-sample-name"> <label for="new-app-sample-name">App name:</label> <input id="new-app-sample-name" type="text" pattern="[-\w\s]+" required value="New App"/> <span class="invalid-message"></span> </div> <div class="new-app-sample-category"> <label for="new-app-sample-category">Category:</label> <select name="category" class="form-control AlwaysEditable" id="new-app-sample-category"> <option value="All" selected>All</option> <option value="Field Service" data-category="Field Service"> Field Service </option> <option value="Inspections &amp; Surveys" data-category="Inspections &amp; Surveys"> Inspections &amp; Surveys </option> <option value="Property Management" data-category="Property Management"> Property Management </option> <option value="Inventory Management" data-category="Inventory Management"> Inventory Management </option> <option value="Customer Engagement" data-category="Customer Engagement"> Customer Engagement </option> <option value="Sales &amp; CRM" data-category="Sales &amp; CRM"> Sales &amp; CRM </option> <option value="Planning &amp; Project Management" data-category="Planning &amp; Project Management"> Planning &amp; Project Management </option> <option value="Human Resources" data-category="Human Resources"> Human Resources </option> <option value="Education &amp; Training" data-category="Education &amp; Training"> Education &amp; Training </option> <option value="Marketing" data-category="Marketing"> Marketing </option> <option value="Legal" data-category="Legal"> Legal </option> <option value="Accounting" data-category="Accounting"> Accounting </option> <option value="Manufacturing" data-category="Manufacturing"> Manufacturing </option> <option value="Operations" data-category="Operations"> Operations </option> <option value="Procurement" data-category="Procurement"> Procurement </option> <option value="Logistics" data-category="Logistics"> Logistics </option> <option value="E-Commerce" data-category="E-Commerce"> E-Commerce </option> <option value="Maintenance" data-category="Maintenance"> Maintenance </option> <option value="Production" data-category="Production"> Production </option> <option value="Quality Management" data-category="Quality Management"> Quality Management </option> <option value="Administration" data-category="Administration"> Administration </option> <option value="Health, Safety and Environment" data-category="Health, Safety and Environment"> Health, Safety and Environment </option> <option value="Productivity" data-category="Productivity"> Productivity </option> <option value="Personal/Fun" data-category="Personal/Fun"> Personal/Fun </option> <option value="Other" data-category="Other"> Other </option> </select> </div> </div> <div class="step-3 step-3-sample"> <div class="step-3-header">Set up your new app</div> <div class="new-app-sample-ok"> <button type="button" id="new-app-sample-ok">Create your app</button> </div> </div> <!-- USE OWN DATA --> <div class="step-2 step-2-data"> <div class="new-app-data-name"> <label for="new-app-data-name">App name:</label> <input id="new-app-data-name" type="text" pattern="[-\w\s]+" required/> <span class="invalid-message"></span> </div> <div class="new-app-data-category"> <label for="new-app-data-category">Category:</label> <select name="category" class="form-control AlwaysEditable" id="new-app-data-category"> <option value="" disabled selected>Choose a category...</option> <option value="Field Service" data-category="Field Service"> Field Service </option> <option value="Inspections &amp; Surveys" data-category="Inspections &amp; Surveys"> Inspections &amp; Surveys </option> <option value="Property Management" data-category="Property Management"> Property Management </option> <option value="Inventory Management" data-category="Inventory Management"> Inventory Management </option> <option value="Customer Engagement" data-category="Customer Engagement"> Customer Engagement </option> <option value="Sales &amp; CRM" data-category="Sales &amp; CRM"> Sales &amp; CRM </option> <option value="Planning &amp; Project Management" data-category="Planning &amp; Project Management"> Planning &amp; Project Management </option> <option value="Human Resources" data-category="Human Resources"> Human Resources </option> <option value="Education &amp; Training" data-category="Education &amp; Training"> Education &amp; Training </option> <option value="Marketing" data-category="Marketing"> Marketing </option> <option value="Legal" data-category="Legal"> Legal </option> <option value="Accounting" data-category="Accounting"> Accounting </option> <option value="Manufacturing" data-category="Manufacturing"> Manufacturing </option> <option value="Operations" data-category="Operations"> Operations </option> <option value="Procurement" data-category="Procurement"> Procurement </option> <option value="Logistics" data-category="Logistics"> Logistics </option> <option value="E-Commerce" data-category="E-Commerce"> E-Commerce </option> <option value="Maintenance" data-category="Maintenance"> Maintenance </option> <option value="Production" data-category="Production"> Production </option> <option value="Quality Management" data-category="Quality Management"> Quality Management </option> <option value="Administration" data-category="Administration"> Administration </option> <option value="Health, Safety and Environment" data-category="Health, Safety and Environment"> Health, Safety and Environment </option> <option value="Productivity" data-category="Productivity"> Productivity </option> <option value="Personal/Fun" data-category="Personal/Fun"> Personal/Fun </option> <option value="Other" data-category="Other"> Other </option> </select> </div> <div class="new-app-data-ok"> <button type="button" id="new-app-data-ok" class="button solid">Choose your data</button> </div> </div> </div> </div> </div> </div> <!-- END NEW APP MODAL --> <div class="modal fade app-display-modal" id="clone-modal" tabindex="-1" role="dialog" aria-labelledby="clone-modal-label" aria-hidden="true" data-backdrop="static"> <div class="modal-dialog"> <div class="modal-content thinScrollbar"> <div class="modal-header"> <button class="AlwaysEditable CloseButtonControl" data-dismiss="modal"> <i class="material">close</i> </button> <h5 class="modal-title" id="clone-modal-label"> <span>Clone your App</span> </h5> </div> <form id="CloneModalForm"> <div class="modal-body thinScrollbar" style="max-height:75vh;overflow:auto"> <p class="form-group appName"> <label for="appName">App name</label> <input name="appName" type="text" class="form-control AlwaysEditable" id="ClonedAppName" required/> <div id="errorMessage"></div> </p> <p class="form-group appCategory"> <label for="category">Category</label> <select name="category" class="form-control AlwaysEditable" id="ClonedAppCategory"> <option value="" disabled selected>Choose a category...</option> <option value="Field Service" data-category="Field Service"> Field Service </option> <option value="Inspections &amp; Surveys" data-category="Inspections &amp; Surveys"> Inspections &amp; Surveys </option> <option value="Property Management" data-category="Property Management"> Property Management </option> <option value="Inventory Management" data-category="Inventory Management"> Inventory Management </option> <option value="Customer Engagement" data-category="Customer Engagement"> Customer Engagement </option> <option value="Sales &amp; CRM" data-category="Sales &amp; CRM"> Sales &amp; CRM </option> <option value="Planning &amp; Project Management" data-category="Planning &amp; Project Management"> Planning &amp; Project Management </option> <option value="Human Resources" data-category="Human Resources"> Human Resources </option> <option value="Education &amp; Training" data-category="Education &amp; Training"> Education &amp; Training </option> <option value="Marketing" data-category="Marketing"> Marketing </option> <option value="Legal" data-category="Legal"> Legal </option> <option value="Accounting" data-category="Accounting"> Accounting </option> <option value="Manufacturing" data-category="Manufacturing"> Manufacturing </option> <option value="Operations" data-category="Operations"> Operations </option> <option value="Procurement" data-category="Procurement"> Procurement </option> <option value="Logistics" data-category="Logistics"> Logistics </option> <option value="E-Commerce" data-category="E-Commerce"> E-Commerce </option> <option value="Maintenance" data-category="Maintenance"> Maintenance </option> <option value="Production" data-category="Production"> Production </option> <option value="Quality Management" data-category="Quality Management"> Quality Management </option> <option value="Administration" data-category="Administration"> Administration </option> <option value="Health, Safety and Environment" data-category="Health, Safety and Environment"> Health, Safety and Environment </option> <option value="Productivity" data-category="Productivity"> Productivity </option> <option value="Personal/Fun" data-category="Personal/Fun"> Personal/Fun </option> <option value="Other" data-category="Other"> Other </option> </select> </p> <p class="copyData copyOption"> <input class="AlwaysEditable" id="CloneCopyData" name="CloneCopyData" type="checkbox" checked/> <span>Make a copy of table data for the new app</span> </p> <p class="copyFiles copyOption"> <input class="AlwaysEditable" id="CloneCopyFiles" name="CloneCopyFiles" type="checkbox" checked/> <span>Make a copy of file data (eg: images) for the new app</span> </p> <p class="copyDataForCoAuthor"> <span>(Warning: Without copying data, the new app will only work if you have access to the owner's data sources)</span> </p> <button id="CloneModalOKButton" type="submit" class="copyApp btn btn-primary AlwaysEditable" data-copydata="true" data-copyfiles="true" data-selectdata="true" data-dismiss="modal">Copy app</button> </div> </form> </div> </div> </div> <div class="modal fade" id="dropbox-modal" tabindex="-1" role="dialog" aria-labelledby="dropbox-modal-label" aria-hidden="true" data-backdrop="static"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <h5 class="modal-title" id="dropbox-modal-label"> <span class="PickerTitle"></span> </h5> </div> <div class="modal-body"> <div id="readOnlyChooser" style="display:none; text-align:center;margin-bottom:20px;"> <input type="checkbox"/> Read-only data? </div> <div id="dropboxtree" style="max-height: 300px;overflow: auto;background-color:#fafafa"> </div> </div> <div class="modal-footer"> <button id="DropboxFullAccessButton" type="button" class="btn" data-dismiss="modal" style="display:none">All Folders</button> <button type="button" class="btn" data-dismiss="modal">Cancel </button> <button id="DropboxModalOKButton" type="button" class="btn btn-primary" data-dismiss="modal">OK </button> </div> </div> </div> </div> <div class="modal fade" id="smartsheet-modal" tabindex="-1" role="dialog" aria-labelledby="smartsheet-modal-label" aria-hidden="true" data-backdrop="static"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <h5 class="modal-title" id="smartsheet-modal-label"> <span class="PickerTitle">Choose a Sheet</span> </h5> </div> <div class="modal-body"> <div style="margin:10px"> <label style="width:100px">Sheet Name:</label> <input id="SmartsheetName" type="text"/> </div> <div style="margin:10px"> <label style="width:100px">Sheet ID:</label> <input id="SmartsheetId" type="text"/> </div> <div style="font-size:smaller;margin-top:10px;"> To find the ID of a sheet, go to the Smartsheet site, open the Properties of the sheet, and copy the ID property. </div> </div> <div class="modal-footer"> <button type="button" class="btn" data-dismiss="modal">Cancel </button> <button id="SmartsheetModalOKButton" type="button" class="btn btn-primary" data-dismiss="modal">OK </button> </div> </div> </div> </div> <div class="modal fade" id="external-modal" tabindex="-1" role="dialog" aria-labelledby="external-modal-label" aria-hidden="true" data-backdrop="static"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <h5 class="modal-title" id="external-modal-label"> <span class="PickerTitle"></span> </h5> </div> <div class="modal-body"> <div id="externaltree" style="max-height: 300px;overflow: auto;background-color:#fafafa"> </div> </div> <div class="modal-footer"> <button type="button" class="btn" data-dismiss="modal">Cancel </button> <button type="button" class="btn btn-primary OKButton" data-dismiss="modal">OK </button> </div> </div> </div> </div> <div class="modal fade" id="treeprovider-modal" tabindex="-1" role="dialog" aria-labelledby="treeprovider-modal-label" aria-hidden="true" data-backdrop="static"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button class="CloseButtonControl" data-dismiss="modal"> <i class="material">close</i> </button> <h5 class="modal-title" id="treeprovider-modal-label"> <span class="PickerTitle"></span> </h5> </div> <div class="modal-body"> <div class="waiting"> <div class="loader"></div> <p>Loading list of files...</p> </div> <div id="provider-tree"> </div> <button type="button" class="btn btn-primary OKButton" data-dismiss="modal">OK </button> </div> </div> </div> </div> <div class="modal fade" id="selectTableSource-modal" tabindex="-1" role="dialog" aria-labelledby="selectTableSource-modal-label" aria-hidden="true" data-backdrop="static"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <h5 class="modal-title" id="selectTableSource-modal-label"> <span>New Table</span> </h5> </div> <div class="modal-body"> <div class="waiting"> <div class="loader">Loading table sources...</div> <button class="add-source btn btn-default">Skip and browse</button> </div> <div class="source-selection"> <div class="suggested-sources"></div> <div class="all-sources"> <p>Add a table from an existing source:</p> <div class="form-group table-source"> <div class="icon"> <label for="worksheet">Source:</label> </div> <div class="dropdown"> <select name="spreadsheet" class="form-control"></select> </div> </div> <div class="form-group worksheet"> <div class="icon"> <label for="worksheet">Worksheet:</label> </div> <div class="dropdown"> <select name="worksheet" class="form-control"></select> </div> </div> <button class="create-table btn btn-default"> <i class="fa fa-plus" aria-hidden="true"></i> <span>Create table from this worksheet</span> </button> <p>Or choose a new source:</p> </div> <div class="new-source"> <button class="browse-sources btn btn-default"> <i class="fa fa-plus" aria-hidden="true"></i> <span class="browse-text">Browse for new source</span> </button> </div> </div> </div> <div class="modal-footer"> <button type="button" class="btn" data-dismiss="modal">Cancel </button> </div> </div> </div> </div> <div class="modal fade" id="sourceselect-modal" tabindex="-1" role="dialog" aria-labelledby="sourceselect-modal-label" aria-hidden="true" data-backdrop="static"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button class="CloseButtonControl" data-dismiss="modal" id="sourceselect-modal-close-button"> <i class="material">close</i> </button> <h5 class="modal-title" id="sourceselect-modal-label"> <span>Get data from...</span> </h5> </div> <div class="modal-body"> <div id="SourceSelector"> </div> </div> </div> </div> </div> <script async nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA==" type="text/javascript" src="/Content/scripts/_shared/external/box-select.js"></script> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA==" type="text/javascript"> var isConcordLoggingPossible = function() { return window.Concord && typeof window.Concord.TrackSimpleEvent === 'function' } var isSampleOwnerId = function(ownerId) { return [ 10305, 71626, 2078346, 5930060 ].indexOf(+ownerId) > -1; } var postCloneRequest = function(props, page) { // Copy the clone request props and track them if (isConcordLoggingPossible()) { window.Concord.TrackSimpleEvent('New App Form', { formAction: 'Submit', }); } var createPendingApp = Boolean(false) && page === "sampleApps"; var url = createPendingApp ? '/api/template/createPendingApp' : '/api/template/createApp'; $('#cloningClickEater').show() verifiedAjaxPost({ url: url, contentType: 'application/json', dataType: 'json', data: JSON.stringify(props) }).fail(function(error) { if (isConcordLoggingPossible() ) { if (createPendingApp) { window.Concord.EndScenario({ result: 'Failure', name: 'CreatePendingApp' }); } else if (!!props.scenario) { window.Concord.EndScenario({ result: 'Failure', scenario: props.scenario }); } } displayErrorModal('Error creating app on the server: ' + error.responseText, true) }).done(function(response) { if (response.Success) { if (isConcordLoggingPossible()) { if (createPendingApp) { window.Concord.EndScenario({ result: 'Success', name: 'CreatePendingApp' }); } else if (!!props.scenario) { window.Concord.EndScenario({ result: 'Success', scenario: props.scenario }); } } if (createPendingApp) { window.location=`/template/showdef?appId=${response.AppName}${props.linkFrom ? `&linkFrom=${props.linkFrom}` : ''}` } else { window.location = response.Url; } } else { if (isConcordLoggingPossible()) { if (createPendingApp) { window.Concord.EndScenario({ result: 'Failure', name: 'CreatePendingApp' }); } else if (!!props.scenario) { window.Concord.EndScenario({ result: 'Failure', scenario: props.scenario }); } } displayErrorModal(response.ErrorDescription) } }) } var loggedIn = false var redirectGuestUser = function () { var returnUrl = encodeURIComponent(window.location.href) window.location.href = '/Account/Login?returnUrl=' + returnUrl } var displayErrorModal = (errorMessage, isPlainText) => { $('#cloningClickEater').show(); $('#errorModal').modal(); $('#templatesButton').on('click', () => { window.open('/templates', '_blank', 'noopener,noreferrer'); if (isConcordLoggingPossible()) { window.Concord.TrackSimpleEvent('Create New App With Error Go To Templates'); } }); $('#ackErrorButton').on('click', () => { if (isConcordLoggingPossible()) { window.Concord.TrackSimpleEvent('Create New App Modal With Error OK Button'); } }); if (isPlainText) { $('#modalErrorText').text(errorMessage); return; } // Get errors in array and filter out empty strings const errors = errorMessage.split('Error: ').filter(x => x); const firstError = errors.shift(); $('#waitingClone').hide(); $('#modalErrorText').text("Error: " + firstError); if(errors && errors.length > 0) { $('.modal-more-errors-holder').show(); $('#errorClick').show(); const errorCount = errors.length; const text = errorCount > 1 ? errorCount + " additional errors." : errorCount + " additional error." $('#moreErrorsAvailableText').text(text); var errorList = '<ul>' errors.forEach((error) => { errorList += '<li>'+ error + '</li>'; }); errorList += '</ul>'; let errorListSafeHtml = '' if (self.trustedTypes && self.trustedTypes.createPolicy) { const policy = self.trustedTypes.createPolicy('safe-error-list', { createHTML: (htmlString) => htmlString }); errorListSafeHtml = policy.createHTML(errorList) } else { errorListSafeHtml = errorList } document.getElementById('moreErrorsText').innerHTML = errorListSafeHtml; $('#seeMoreArrow').on('click', () => { $('#moreErrorsText').show(); $('#seeLessArrow').show(); $('#seeMoreArrow').hide(); }); $('#seeLessArrow').on('click', () => { $('#moreErrorsText').hide(); $('#seeLessArrow').hide(); $('#seeMoreArrow').show(); }); } } var cloneButtonClickHandlerLoggedIn = function (data) { // The data object should have keys that match up // with the .CloneButton data-* attributes. var appName = data.appname var appShortName = data.shortname var appDescription = data.description var dataCopyOption = data.copyoption var isChatbot = data.ischatbot var appLogo = data.logo var ownerId = data.owner var appId = data.appid var page = data.page var isShared = data.isshared prepareCloneDialog(appName, appShortName, appLogo, ownerId, appDescription, dataCopyOption, isChatbot, isShared, appId, page) return $('#clone-modal').modal() } var prepareCloneDialog = function (sourceAppName, sourceAppShortName, logoUrl, ownerId, sourceAppDescription, dataCopyOption, isChatbot, isShared, appId, page) { // Get modal element and app name var modal = $('#clone-modal') modal.find('.modal-title img').attr('src', logoUrl) modal.find('#ClonedAppName').val(sourceAppShortName) modal.find('#ClonedAppDescription').val(sourceAppDescription) // Setup category list var category = null var categoryText = null var categoryList = modal.find('#ClonedAppCategory') categoryList.val('') categoryList.find('.temp-category').remove() modal.find('.samples').hide().removeClass('chatbotSamples').removeClass('mobileSamples').addClass(isChatbot ? 'chatbotSamples' : 'mobileSamples') // Show appropriate buttons/options if starting from an existing app or not var createFromScratch = (sourceAppName == '') && (('MyNewApp' == sourceAppShortName) || ('MyNewChatbot' == sourceAppShortName)) if (createFromScratch) { modal.find('#App_Blank_Template').addClass('active') modal.find('#CloneModalOKButton').hide() } else { modal.find('#CloneModalOKButton').show() modal.find('#CloneSelectData').hide() category = $('.app-card.' + sourceAppName + ' .category').attr('data-category') categoryText = $('.app-card.' + sourceAppName + ' .category').text() } // Select existing app category (if starting from an app template) category = category || 'Property Management' categoryText = categoryText || 'Property Management' if(!categoryList.find('option[value="' + category + '"]').length) { categoryList.append( $('<option>', { value: category, text: categoryText, class: 'temp-category'})) } if ('category' != '') categoryList.val(category) var copyMyOwnApp = (ownerId > 0) && (ownerId == -1) modal.find('.copyData').hide() modal.find('.copyFiles').hide() modal.find('.copyDataForCoAuthor').hide() if (copyMyOwnApp || dataCopyOption || isShared) { modal.find('.copyData').show() modal.find('.copyFiles').show() } if (isShared) { modal.find('.copyDataForCoAuthor').show() } // first bind the OK button in the clone modal dialog modal.find('[type="submit"]').off('click').on('click', function (e) { // Get app info var shortName = modal.find('#ClonedAppName').val() // Make sure app name is valid if (shortName === null || shortName.length === 0 || $('#ClonedAppName').is(':not(:valid)')) { return } // Get rest of the app's information var category = modal.find('option[value="' + modal.find('#ClonedAppCategory').val() + '"]').attr('data-category') var description = modal.find('#ClonedAppDescription').val() var dataSource = modal.find('#ClonedAppDataSource').val() var copyData = $(this).attr('data-copydata') == 'true' var copyFiles = $(this).attr('data-copyfiles') == 'true' var selectData = $(this).attr('data-selectdata') == 'true' sourceAppName = sourceAppName || $(this).attr('data-appid') // Close the dialog modal.each(function () { $('.modal-background').first().fadeOut(200, function () { $(this).remove() }) $(this).fadeOut(200) }) var scenario = isConcordLoggingPossible()? window.Concord.GetScenario() : undefined if (!loggedIn) { redirectGuestUser() } else if (sourceAppName) { var createPendingApp = Boolean(false) && page === "sampleApps"; var request = createPendingApp ? { AppId: appId, shortName: shortName, linkFrom: 'CopyApp', copyData: copyData, copyFiles: copyFiles, category: category, description: description, authSource: dataSource, } : { shortName: shortName, linkFrom: 'CopyApp', copyFrom: sourceAppName, copyData: copyData, copyFiles: copyFiles, category: category, description: description, appSource: dataSource, scenario: scenario } // Clone from existing app postCloneRequest(request, page) } else if (selectData) { // Launch sheet selector var callback = function (fileName, filePath, isReadOnly, dataSourceName, provider, qualifier) { postCloneRequest({ shortName: shortName, linkFrom: 'MakeNewApp', dataSourceName: dataSourceName, provider: provider, filePath: filePath, fileName: fileName, qualifier: qualifier, category: category, description: description, appSource: dataSource, scenario: scenario }) } var options = { returnPathAsUrl: false, fileType: filePickerOptions.PICK_TABLE, userId: ownerId } // var clickDefault = ($('#SourceSelector .DataSourceButton').length <= 1) clickDefault = false selectSheetSource(options, callback, clickDefault) } else { // Create blank new app postCloneRequest({ shortName: shortName, linkFrom: 'MakeNewApp', category: category, description: description, scenario: scenario }) } }) // When category dropdown is changed, update template apps modal.find('#ClonedAppCategory').off('change').on('change', function () { if (createFromScratch && modal.find('.samples').length) { var selectedCategory = $(this).val() if (selectedCategory != '') { modal.find('#CloneSelectData').slideDown() } var container = modal.find('.samples .appContainer') container.slideUp() container.filter('.' + selectedCategory).slideDown() } }) // Change whether data gets copied based on checkbox state modal.find('#CloneCopyData').off('change').on('change', function () { modal.find('.copyApp').attr('data-copydata', $(this).prop('checked')) if (!$(this).prop('checked') && modal.find('#CloneCopyFiles').prop('checked')) { // make sure that CloneCopyFiles is off modal.find('#CloneCopyFiles').prop('checked', false) modal.find('#CloneCopyFiles').change() } }) // Clicking on checkbox label also changes checkbox modal.find('.copyData > span').off('click').on('click', function () { modal.find('#CloneCopyData').prop('checked', function( i, val ) { return !val }) modal.find('#CloneCopyData').change() }) modal.find('#CloneCopyFiles').off('change').on('change', function () { modal.find('.copyApp').attr('data-copyfiles', $(this).prop('checked')) }) // Clicking on checkbox label also changes checkbox modal.find('.copyFiles > span').off('click').on('click', function () { modal.find('#CloneCopyFiles').prop('checked', function( i, val ) { return !val }) modal.find('#CloneCopyFiles').change() }) } $(function () { $('#clone-modal').on('shown.bs.modal', function () { $('#ClonedAppName').focus().select() }) $('#clone-modal .TemplateOption').on('click', function (e) { $('#clone-modal #TemplateUL li').removeClass('active') $(this).closest('li').addClass('active') e.stopPropagation() e.preventDefault() }) $('#clone-modal #CloneModalForm').on('submit', function () { return false }) $('#ClonedAppName').on('input', function () { const tooLong = $(this).val().length > 50 +0 const unsupportedChars = $(this).val().match(/[^-\w\s]/) if (tooLong || unsupportedChars) { $('#CloneNameControl').removeClass('has-success') $('#CloneNameControl').addClass('has-error') $('#errorMessage').removeClass('sr-only') $('#CloneModalOKButton').prop('disabled', true) if (unsupportedChars) { $('#errorMessage').text('App name cannot contain special characters.') } else { $('#errorMessage').text('App name exceeds length limit of ' + 50 + ' characters.') } } else { $('#CloneNameControl').removeClass('has-error') $('#CloneNameControl').addClass('has-success') $('#errorMessage').text('') $('#errorMessage').addClass('sr-only') $('#CloneModalOKButton').prop('disabled', false) } }) $('#clone-modal .cancel').on('click', function () { if (isConcordLoggingPossible()) { window.Concord.TrackSimpleEvent('New App Form', { formAction: 'Cancel', }); } }) $('#clone-modal .CloseButtonControl').on('click', function () { if(isConcordLoggingPossible()){ var scenario = window.Concord.GetScenario(); if(scenario && scenario.scenarioName == "CreatePendingApp"){ window.Concord.EndScenario({ name: "CreatePendingApp", result: 'Abandon', abandonReason: 'UserNavigatedAway' }); } } }) $(document) .on('click', '.CloneButton', function (e) { e.stopPropagation() e.preventDefault() var data = $(this).data() var appName = data.appname var appShortName = data.shortname var storyUrl = data.storyurl var ownerId = data.owner || ((appName || '').split('-')[1]) || '-1'; if (loggedIn) { // Launch the modal cloneButtonClickHandlerLoggedIn(data) // Handle the different cases for tracking. There are two // different events to track. The first is when the user clicks on // the "New mobile app" button on the "My Apps" page. The second case // is all other "copy app" button clicks. if ($(this).attr('id') == 'NewAppButton') { if (isConcordLoggingPossible()) { window.Concord.TrackSimpleEvent('New App Form', { formAction: 'Open', }); } } else { if (isConcordLoggingPossible()) { var isSampleApp = isSampleOwnerId(ownerId); window.Concord.TrackSimpleEvent('Copy Signed In', { appName: isSampleApp ? appName : '', copyType: isSampleApp ? 'Sample' : 'Account', }); } } } else { // When the user is not logged in, we want to redirect them to the // sign-in page. When they're done logging in, we redirect them to // a page, depending on where they came from. If they are copying a // sample app (i.e. something that has a story page) then we will // redirect them to the app's story page and pass in the copy=1 param // to pop open the clone dialog, so they don't have to click it // again. Otherwise, just redirect them back to the current url. var redirectUrl if (storyUrl) { redirectUrl = storyUrl + '&copy=1' } else { redirectUrl = window.location.pathname + window.location.search } redirectUrl = '/Account/Login?returnUrl=' + encodeURIComponent(redirectUrl) if (isConcordLoggingPossible()) { var isSampleApp = isSampleOwnerId(ownerId); var props = { appName: isSampleApp ? appName : '', copyType: isSampleApp ? 'Sample' : 'Account', }; var scenario = undefined; var redirectCallback = function(success) { window.location.href = redirectUrl; }; window.Concord.TrackSimpleEvent('Copy Not Signed In', props, scenario, redirectCallback); } else { window.location.href = redirectUrl; } } }) }) </script> <!-- NEW APP MODAL SCRIPTS --> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA=="> (function () { function updateModal(props) { $('#new-app-modal').data('props', props) var modal = $('#new-app-modal .modal-body') modal.children().hide() if (props.step === 1) { modal.children('.step-1').show() } else if (props.step === 2 && props.type === 'sample') { var sampleStep = modal.children('.step-2-sample') sampleStep.show() var sampleApp = sampleStep.children('.new-app-sample-app') sampleApp.hide() if (props.category === 'All') { sampleApp.show() sampleApp.find('.app:not(.selectData)').show() } else { sampleApp.find('.app').hide() if (props.category) { sampleApp.show() sampleApp.find('.app[data-category="' + props.category + '"]:not(.selectData)').show() } else { sampleStep.find('#new-app-sample-category').val(null) } } } /** else if (props.step === 3 && props.type === 'sample') { var sampleStep = modal.children('.step-3-sample') sampleStep.show() } **/ else if (props.step === 2 && props.type === 'data') { var dataStep = modal.children('.step-2-data') dataStep.show() dataStep.find('#new-app-data-name').val('New App') } } window.openNewAppModal = function (e) { if (e) { e.preventDefault() e.stopImmediatePropagation() } $('#new-app-modal').modal() updateModal({ step: 1 }) }; window.startWithData = function (e) { if (e) { e.preventDefault() e.stopImmediatePropagation() } if (isConcordLoggingPossible()) { window.Concord.EndScenario({ result: 'Success', name: 'CreateApp' }); window.Concord.StartScenario('CreateAppFromDataSource') } updateModal({ step: 2, type: 'data' }) } var oldOnBeforeUnload = window.onbeforeunload ? window.onbeforeunload : function () {}; window.onbeforeunload = function () { // this will run the onbeforeunload that was bound previously, if there was one oldOnBeforeUnload.call(window); if (isConcordLoggingPossible()) { window.Concord.TrackSimpleEvent('User Navigated Away'); window.Concord.EndScenario({ result: 'Abandon', abandonReason: 'UserNavigatedAway' }); } } $(document) .on('click', '#NewAppButton', function(e) { window.openNewAppModal() if (null && null) { window.startWithData() } }) .on('click', '#create-new-app-modal-close', function(e) { if (isConcordLoggingPossible()) { window.Concord.TrackSimpleEvent('Close Create New App Modal') window.Concord.EndScenario({ result: 'Abandon', abandonReason: 'ClosedModal' // don't pass in name: this modal has a few scenarios so end whichever one is active }); } }) .on('click', '#new-app-start-with-sample', function (e) { e.preventDefault() e.stopImmediatePropagation() // pre-select the 'All' category const selectedCategory = 'All' $('#new-app-sample-category').val(selectedCategory) if (isConcordLoggingPossible()) { window.Concord.TrackSimpleEvent('Click New App Start With Sample') window.Concord.EndScenario({ result: 'Success', name: 'CreateApp' }); window.Concord.StartScenario('CreateAppFromSampleApp') } updateModal({ step: 2, type: 'sample', category: selectedCategory }) }) .on('click', '#new-app-start-with-data', function (e) { e.preventDefault() e.stopImmediatePropagation() window.startWithData() }) .on('click', '#new-app-start-with-spec', function (e) { e.preventDefault() e.stopImmediatePropagation() if (isConcordLoggingPossible()) { window.Concord.TrackSimpleEvent('Click New App Start With Idea') window.Concord.EndScenario({ result: 'Success', name: 'CreateApp' }); // start on /spec page CreateAppFromIdea so onbeforeunload here doesn't abandon it } window.location.href = '/spec' }) .on('change', '#new-app-sample-category', function () { updateModal({ step: 2, type: 'sample', category: $(this).val() }) }) .on('click', '#new-app-modal .app', function (e) { e.preventDefault() e.stopImmediatePropagation() var category = $('#new-app-modal').data('props').category var appid = $(this).data('appid') var appname = $(this).data('appname') if ($('#new-app-sample-name').val() === 'New App') { $('#new-app-sample-name').val('Copy of ' + appname) } updateModal({ step: 2, type: 'sample', category: category, appid: appid, appname: appname }) document.getElementById('new-app-sample-ok').click() }) .on('click', '#new-app-sample-ok', function (e) { e.preventDefault() e.stopImmediatePropagation() var category = $('#new-app-modal').data('props').category var appid = $('#new-app-modal').data('props').appid var shortName = $('#new-app-sample-name').val() var dataSource = $('#new-app-sample-dataSource').val() if (!shortName || $('#new-app-sample-name').is(':not(:valid)')) { return } if (!loggedIn) { redirectGuestUser() return } var scenario = isConcordLoggingPossible() ? window.Concord.GetScenario() : undefined postCloneRequest({ shortName: shortName, linkFrom: 'CopyApp', copyFrom: appid, copyData: true, category: category, description: '', appSource: dataSource, scenario: scenario }) $('#new-app-modal').modalclose() }) .on('click', '#new-app-data-ok', function (e) { e.preventDefault() e.stopImmediatePropagation() var category = $('#new-app-data-category').val() var shortName = $('#new-app-data-name').val() var dataSource = $('#new-app-data-dataSource').val() if (!shortName || $('#new-app-data-name').is(':not(:valid)')) { return } if (!loggedIn) { redirectGuestUser() return } var scenario = isConcordLoggingPossible() ? window.Concord.GetScenario() : undefined // Setup sheet selector var callback = function (fileName, filePath, isReadOnly, dataSourceName, provider, qualifier) { postCloneRequest({ shortName: shortName, linkFrom: 'MakeNewApp', dataSourceName: dataSourceName, provider: provider, filePath: filePath, fileName: fileName, qualifier: qualifier, category: category, copyData: false, description: '', appSource: dataSource, scenario: scenario }) } var options = { returnPathAsUrl: false, fileType: filePickerOptions.PICK_TABLE, userId: '-1' } clickDefault = true // Launch sheet selector selectSheetSource(options, callback, clickDefault) $('#new-app-modal').modalclose() $('#sourceselect-modal-close-button').on('click', function () { if (isConcordLoggingPossible()) { window.Concord.EndScenario({ result: 'Abandon', abandonReason: 'ClosedModal' }); } }) $('#sourceselect-modal-add-datasource-button').on('click', function () { if (isConcordLoggingPossible()) { window.Concord.EndScenario({ result: 'Abandon', abandonReason:'AddNewDataSource' }); } }) }) })() </script> <!-- END NEW APP MODAL SCRIPTS --> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA==" src="/Scripts/sp-theme/root.js"></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/090510102d9f686cce5db049efd92db035e66231ef0381147401ce867b7c9e02.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/2e12937f9cad1713615022f1dac0f3ece070d2bd2c1b4869adb27283343662b3.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/3b9506d1a02fbf90e8e73428dbb6956d182fab2498986fba5d526c6eab55f4fb.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/0e99eb8d8e2893f08bba694dd376a0534674563bd727b854af76ea853957d6a7.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/8ad81b62b187d3e1f280067fb829766fa4242c7444221087895247b732858156.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/c2c5184ed5b01c5b9e772bfdb27577ee84e61bcd0a0c75ad92b049a39d2b2f63.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/1e101732b35668c699cdbd3350e5edcdcfea38d66e641e5e5c4db989f8dc6e75.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/403ad3bdf3c5fce0c26a304d3dcc6223260d7bd8a5c170fac739880c19f73816.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/e2817dac5a4c181ca0f5611e5bc42d099679241a0a40b0f2a45b515144cf742f.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/6f7fa028e348569b4380e330a8a63cf07687cc3ae0c7343ff09430158c589f08.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/44dcf8088b237651c6f1b776ef8738d4e048226809929b53a5acd34b69eac83d.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/bd3e6c83fa98d2865910cb48cd16539a7635c4a765b11567f24a859fed6523d8.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/e5b1786962b247f7bdcc9bd0b3d5fb3dd7883e844b5361cfb6467431334739ab.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/86ac34716e755c863de9416fa364ac815ba6399acdebe82416f4b07071f84317.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/8b651b1080583b462190a1a671c3e8cbf939086b6fbabced19e6ca2f94272138.js" ></script> <script nonce="6AlEb4/VyxSb1fEb+LKfEA==" src="https://www.appsheet.com/assets/0cfe6e4a72d7d7581601af97977e1187df05cdad81e993e9b5e35f8dbb7d8157.js" ></script> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA=="> $(function () { initPreviewFrame('5486a6ee-6293-4dbc-b24f-d51413fe1be6', false, false); var url = new URL(window.location.href); var copyParam = url.searchParams.get("copy"); // We can be directed to the appstory page with the command to // automatically open up the clone modal. This is done by passing // copy=1 to the parameters. if (copyParam !== null) { // Don't actually click the .CloneButton, since that will // trigger events to be tracked. Call the click handler // directly so this doesn't happen. The arguments passed in // are the same as the data-* attributes on the .CloneButton // div. var cloneModal = cloneButtonClickHandlerLoggedIn({ appname: 'OfficeManager3-1224517', shortname: 'Office Manager', owner: '1334275', logo: 'https://storage.googleapis.com/cloud-icons/vector/building_general.png', storyurl: 'https://www.appsheet.com/templates/Organize-and-track-office-resources-during-back-to-work?appGuidString=5486a6ee-6293-4dbc-b24f-d51413fe1be6' }); } // Here we have chosen to click the OKclone button to remove // friction in certain circumstances. (new user signup for one) if (copyParam == "2") { $(cloneModal).ready(() => { $('#CloneModalOKButton').trigger("click"); }); } }) </script> <!-- MORE TRACKING --> <!-- Google tag (gtag.js) --> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA=="> window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); function glueCookieNotificationBarLoaded() { (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'}); var firstScript=d.getElementsByTagName(s)[0], dl=l!='dataLayer'?'&l='+l:''; var script = d.createElement(s); script.async = true; var url = 'https://www.googletagmanager.com/gtm.js?id='+i+dl; if (self.trustedTypes && self.trustedTypes.createPolicy) { var policy = self.trustedTypes.createPolicy('gtm-load-fix', { createScriptURL: function(_ignored) { return url; } }); script.src = policy.createScriptURL('_ignored'); } else { script.src = url; } firstScript.parentNode.insertBefore(script, firstScript); })(window,document,'script','dataLayer','GTM-MXWLX3PF'); } </script> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA==" src="/Content/scripts/_shared/external/cookienotificationbar.min.js" data-glue-cookie-notification-bar-autoload=false> </script> <script nonce="6AlEb4/VyxSb1fEb&#x2B;LKfEA=="> document.addEventListener("DOMContentLoaded", function() { new window.glue.CookieNotificationBar(document.getElementById('cookieConsentContainer'), { category: window.glue.CookieNotificationBar.category.TWO_A, siteId: "www.appsheet.com", language: 'en', }); }); </script> </div> <div id="cookieConsentContainer"></div> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10