{"id":609,"date":"2023-06-29T15:26:00","date_gmt":"2023-06-29T15:26:00","guid":{"rendered":"https:\/\/automatethemundane.com\/index.php\/2023\/06\/29\/hours-tracker-part-6-time-sheet\/"},"modified":"2023-06-29T15:26:00","modified_gmt":"2023-06-29T15:26:00","slug":"hours-tracker-part-6-time-sheet","status":"publish","type":"post","link":"https:\/\/automatethemundane.com\/index.php\/2023\/06\/29\/hours-tracker-part-6-time-sheet\/","title":{"rendered":"Hours Tracker Part 6- Time Sheet"},"content":{"rendered":"\n<p class=\"has-text-color\" style=\"color: rgb(0, 0, 0)\">7\/6\/2023<\/p>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n<h1 class=\"wp-block-heading\">The Why<\/h1>\n\n\n<p class=\"has-text-color\" style=\"color: rgb(0, 0, 0)\">This part of the guide will go over the Canvas app portion of the build. The goal here is to build a very simple canvas app that will allow the users to add, update, and remove their hours. It will also serve as the location for leads to approve hours. <\/p>\n\n\n<h1 class=\"wp-block-heading\">The How<\/h1>\n\n\n<h2 class=\"wp-block-heading\">Initial Setup<\/h2>\n\n\n<ol class=\"wp-block-list\">\n<li>Create a blank Canvas app<\/li>\n\n\n\n<li>Connect in the User, Hours, Project, and Team Tables. \n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2023\/08\/untitled.png\" alt=\"\"\/><\/figure>\n<\/li>\n\n\n\n<li>On the App OnStart add the following variables. The varUser and varTeam are used to minimize the amount of looks to be done later. \n<pre class=\"wp-block-code\"><code>\/\/Get User and Team Data\nSet(\n    varUser,\n    User()\n);\nSet(\n    varteam,\n    LookUp(\n        Users,\n        &#039;Primary Email&#039; = varUser.Email\n    ).Team.&#039;Team Name&#039;\n);<\/code><\/pre>\n<\/li>\n\n\n\n<li>Add a Date Picker Control and set it as required. Name it calinputTS.\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2023\/08\/untitled-1-1024x210.png\" alt=\"\"\/><\/figure>\n\n\n<p class=\"has-background\" style=\"background-color: rgb(241, 241, 239)\">&#x1f4a1; Note: I am using a newer modern control in this application. I really love the look and feel of the new date picker. <\/p>\n<\/li>\n\n\n\n<li>Set the Value to Today()<\/li>\n<\/ol>\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2023\/08\/untitled-2-1024x215.png\" alt=\"\"\/><\/figure>\n\n\n<pre class=\"wp-block-code\"><code>Today()<\/code><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Logic Setup<\/h2>\n\n\n<ol class=\"wp-block-list\">\n<li>We now need to configure a few variables and collections to get the data and time box it. <\/li>\n\n\n\n<li>The first two variables we will create set the start and stop dates based on the calendrer selection. \n<pre class=\"wp-block-code\"><code>Set(\n    selectedStartDate,\n    DateAdd(\n        calinputTS.Value,\n        1 - Weekday(\n            calinputTS.Value,\n            StartOfWeek.Monday\n        )\n    )\n);\nSet(\n    selectedEndDate,\n    DateAdd(\n        calinputTS.Value,\n        7 - Weekday(\n            calinputTS.Value,\n            StartOfWeek.Monday\n        )\n    )\n);<\/code><\/pre>\n\n\n<p class=\"has-background\" style=\"background-color: rgb(241, 241, 239)\">&#x1f4a1; Note: for all of the configurations here my week day starts on Monday. <\/p>\n<\/li>\n\n\n\n<li>The Next two are the collections that gather the users authorized projects and any hours they may have already entered. \n<pre class=\"wp-block-code\"><code>ClearCollect(\n    colAuthorizedProjects,\n    LookUp(\n        Users,\n        &#039;Primary Email&#039; = varUser.Email\n    ).&#039;Projects (ew_SystemUser_ew_Project_ew_Project)&#039;\n);\nClearCollect(\n    colcurrentHours,\n    Filter(\n        Hours,\n        &#039;Date Worked&#039; &gt;= selectedStartDate &amp;&amp; &#039;Date Worked&#039; &lt;= selectedEndDate &amp;&amp; User.&#039;Primary Email&#039; = varUser.Email\n    )\n);<\/code><\/pre>\n\n\n<p class=\"has-background\" style=\"background-color: rgb(241, 241, 239)\">&#x1f4a1; Note: the \u201c).&#039;Projects (ew_SystemUser_ew_Project_ew_Project)&#039;\u201d is the N:N relationship that we setup in <\/p>\n<\/li>\n\n\n\n<li>The Next collection is collection colworkdays. It is the time box so that when we add the days to the gallery only the days that fall within it show up. \n<pre class=\"wp-block-code\"><code>Clear(colworkdays);\nForAll(\n    Sequence(7),\n    Collect(\n        colworkdays,\n        {\n            DayName: Text(\n                DateAdd(\n                    calinputTS.Value,\n                    Value - Weekday(\n                        calinputTS.Value,\n                        StartOfWeek.Monday\n                    )\n                )\n            ),\n            Date: DateAdd(\n                calinputTS.Value,\n                Value - Weekday(\n                    calinputTS.Value,\n                    StartOfWeek.Monday\n                )\n            )\n        }\n    )\n)<\/code><\/pre>\n<\/li>\n\n\n\n<li>All together these are loaded into the OnVisible property of the timesheet screen\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2023\/08\/untitled-3-1024x445.png\" alt=\"\"\/><\/figure>\n\n\n<pre class=\"wp-block-code\"><code>Set(\n    selectedStartDate,\n    DateAdd(\n        calinputTS.Value,\n        1 - Weekday(\n            calinputTS.Value,\n            StartOfWeek.Monday\n        )\n    )\n);\nSet(\n    selectedEndDate,\n    DateAdd(\n        calinputTS.Value,\n        7 - Weekday(\n            calinputTS.Value,\n            StartOfWeek.Monday\n        )\n    )\n);\nClearCollect(\n    colAuthorizedProjects,\n    LookUp(\n        Users,\n        &#039;Primary Email&#039; = varUser.Email\n    ).&#039;Projects (ew_SystemUser_ew_Project_ew_Project)&#039;\n);\nClearCollect(\n    colcurrentHours,\n    Filter(\n        Hours,\n        &#039;Date Worked&#039; &gt;= selectedStartDate &amp;&amp; &#039;Date Worked&#039; &lt;= selectedEndDate &amp;&amp; User.&#039;Primary Email&#039; = varUser.Email\n    )\n);\nClear(colworkdays);\nForAll(\n    Sequence(7),\n    Collect(\n        colworkdays,\n        {\n            DayName: Text(\n                DateAdd(\n                    calinputTS.Value,\n                    Value - Weekday(\n                        calinputTS.Value,\n                        StartOfWeek.Monday\n                    )\n                )\n            ),\n            Date: DateAdd(\n                calinputTS.Value,\n                Value - Weekday(\n                    calinputTS.Value,\n                    StartOfWeek.Monday\n                )\n            )\n        }\n    )\n)<\/code><\/pre>\n<\/li>\n<\/ol>\n\n\n<p class=\"has-text-color\" style=\"color: rgb(0, 0, 0)\">We also need to add logic to the date picker so that when a date is selected, the colection is updated. Copy the above logic and place it in the OnChange of the date picker. <\/p>\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2023\/08\/untitled-4-1024x338.png\" alt=\"\"\/><\/figure>\n\n\n<h2 class=\"wp-block-heading\">Gallery Configuration<\/h2>\n\n\n<ol class=\"wp-block-list\">\n<li>There are two galleries to be added to the time sheet screen. The first is called galProjectsTS, it is the gallery for all the projects and will use the collection colAuthorizedProjects. The second will be a sub gallery in the galProjectsTS and be called galHoursTS, it is where all the hours are to be entered and is fed by the colworkdays collection. <\/li>\n<\/ol>\n\n\n<h2 class=\"wp-block-heading\">galProjectsTS<\/h2>\n\n\n<ol class=\"wp-block-list\">\n<li>Add a blank vertical gallery and call it galProjectsTS set the items to colAuthorizedProjects<\/li>\n\n\n\n<li>Add a label control and call it lblProjectNameHS. Set it to \n<pre class=\"wp-block-code\"><code>ThisItem.&#039;Project Name&#039;<\/code><\/pre>\n<\/li>\n<\/ol>\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2023\/08\/untitled-5-1024x242.png\" alt=\"\"\/><\/figure>\n\n\n<h2 class=\"wp-block-heading\">galHoursTS<\/h2>\n\n\n<ol class=\"wp-block-list\">\n<li>Insert a blank hortintoal gallery inside galProjectsTS. Name it galHoursTS<\/li>\n\n\n\n<li>Set the Items to colworkdays\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2023\/08\/untitled-6-1024x356.png\" alt=\"\"\/><\/figure>\n<\/li>\n\n\n\n<li>Add a text label to the gallery and name it lblDayHoursTS. Adjust the label and the gallery to make it look like the following. \n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2023\/08\/untitled-7-1024x282.png\" alt=\"\"\/><\/figure>\n<\/li>\n\n\n\n<li>Update the Text for the label to the following\n<pre class=\"wp-block-code\"><code>Text(\n    ThisItem.Date,\n    &quot;[$-en-US]ddd ,mmm dd&quot;\n)<\/code><\/pre>\n<\/li>\n\n\n\n<li>Add a text input and name it inputHoursTS. Set the input to be a number value and the hint text to 0<\/li>\n\n\n\n<li>The next step is to add the default value of the input. This will search the collection to see if there are any hours for that given day. \n<ol class=\"wp-block-list\">\n<li>Set the Default value of the input to the following\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2023\/08\/untitled-8-1024x255.png\" alt=\"\"\/><\/figure>\n\n\n<pre class=\"wp-block-code\"><code>LookUp(\n    colcurrentHours,\n    Project.&#039;Project Name&#039; = lblProjectNameHS.Text &amp;&amp; &#039;Date Worked&#039; = ThisItem.Date\n).&#039;Hours (eca_hours)&#039;<\/code><\/pre>\n\n\n<p class=\"has-background\" style=\"background-color: rgb(241, 241, 239)\">&#x1f4a1; Note: Yours hours name will be different then mine. <\/p>\n<\/li>\n<\/ol>\n<\/li>\n\n\n\n<li>Next we will set what happens when an entry is either added or removed from the input. I have chosen to do this in the on change of the input. This was the simplest method when working in the sub galleries. \n<ol class=\"wp-block-list\">\n<li>Set the Delay Output to true. This will ensure that the patch dose not happen until the user clicks out of the text box. \n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/automatethemundane.com\/wp-content\/uploads\/2023\/08\/untitled-9-1024x226.png\" alt=\"\"\/><\/figure>\n<\/li>\n\n\n<li>Set the On Change to the following \n<pre class=\"wp-block-code\"><code>\/\/ First, look for an existing record and set it in &quot;existingRecord&quot;\nWith(\n    {\n        existingRecord: LookUp(\n            colcurrentHours,\n            &#039;Date Worked&#039; = ThisItem.Date &amp;&amp; Project.&#039;Project Name&#039; = galProjectsTS.Selected.&#039;Project Name&#039; &amp;&amp; User.&#039;Primary Email&#039; = LookUp(\n                Users,\n                &#039;Primary Email&#039; = varUser.Email\n            ).&#039;Primary Email&#039;\n        )\n    },\n    If(\n        !IsBlank(inputHoursTS.Text) || Value(inputHoursTS.Text) = 0,\n\/\/ Patch the record:\n        Patch(\n            Hours,\n    \/\/ If an existing record was found, update it; if not, create a new one:\n            If(\n                IsBlank(existingRecord),\n                Defaults(Hours),\n                existingRecord\n            ),\n            {\n                &#039;Hours (ew_hours)&#039;: Value(inputHoursTS.Text),\n                Project: LookUp(\n                    Projects,\n                    &#039;Project Name&#039; = galProjectsTS.Selected.&#039;Project Name&#039;\n                ),\n                &#039;Date Worked&#039;: ThisItem.Date,\n                User: LookUp(\n                    Users,\n                    &#039;Primary Email&#039; = varUser.Email\n                )\n            }\n        ),\n    \/\/Remove the record if removed from input\n        If(\n            !IsBlank(existingRecord),\n            Remove(\n                Hours,\n                existingRecord\n            )\n        )\n    )\n);\nClearCollect(\n    colcurrentHours,\n    Filter(\n        Hours,\n        &#039;Date Worked&#039; &gt;= selectedStartDate &amp;&amp; &#039;Date Worked&#039; &lt;= selectedEndDate &amp;&amp; User.&#039;Primary Email&#039; = varUser.Email\n    )\n);<\/code><\/pre>\n<\/li>\n\n\n<li>Set the Display mode as follows. This will disable the input if the lead has approved the hours. \n<pre class=\"wp-block-code\"><code>If(\n    LookUp(\n        colcurrentHours,\n        Project.&#039;Project Name&#039; = lblProjectNameHS.Text &amp;&amp; &#039;Date Worked&#039; = ThisItem.Date\n    ).&#039;Lead Verify&#039; = true,\n    DisplayMode.Disabled,\n    DisplayMode.Edit\n)<\/code><\/pre>\n<\/li>\n<\/ol>\n<\/li>\n<\/ol>\n\n\n<p class=\"has-text-color\" style=\"color: rgb(0, 0, 0)\">The core of the canvas app is now done. In the next guide we will add in supervisors approval. <\/p>\n\n","protected":false},"excerpt":{"rendered":"<p>7\/6\/2023 The Why This part of the guide will go over the Canvas app portion of the build. The goal here is to build a very simple canvas app that will allow the users to add, update, and remove their hours. It will also serve as the location for leads to approve hours. The How [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":610,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[18,25],"class_list":["post-609","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tag-canvas-app","tag-dataverse","entry","has-media"],"jetpack_featured_media_url":"https:\/\/automatethemundane.com\/wp-content\/uploads\/2023\/08\/photo-1553272787-2bbfd028da99-scaled.jpg","_links":{"self":[{"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/posts\/609","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/comments?post=609"}],"version-history":[{"count":0,"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/posts\/609\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/media\/610"}],"wp:attachment":[{"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/media?parent=609"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/categories?post=609"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/automatethemundane.com\/index.php\/wp-json\/wp\/v2\/tags?post=609"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}