[{"data":1,"prerenderedAt":1479},["ShallowReactive",2],{"blog-\u002Fblog\u002F2024\u002F04\u002Fhow-to-build-an-application-with-node-red-dashboard-2":3},{"id":4,"title":5,"body":6,"description":12,"extension":1469,"meta":1470,"navigation":147,"path":1475,"seo":1476,"stem":1477,"__hash__":1478},"blog\u002Fblog\u002F2024\u002F04\u002Fhow-to-build-an-application-with-node-red-dashboard-2.md","How to Build An Application With Node-RED Dashboard 2.0",{"type":7,"value":8,"toc":1456},"minimark",[9,13,22,27,35,43,47,56,59,64,79,86,95,102,106,109,124,297,308,312,324,331,356,360,363,388,395,402,469,482,486,493,503,510,526,530,545,562,569,574,1380,1390,1394,1401,1412,1415,1419,1422,1453],[10,11,12],"p",{},"In this guide, we'll build a Todo application to guide you through the features and explain how you can build rich and dynamic applications too. It shows many of the features that make Dashboard 2.0 great, and why you should use it over the deprecated node-red-dashboard.",[10,14,15,16,21],{},"If you're new to Dashboard 2.0, refer to our blog post ",[17,18,20],"a",{"href":19},"\u002Fblog\u002F2024\u002F03\u002Fdashboard-getting-started\u002F","Getting Started with Dashboard 2.0"," to install and get things started.",[23,24,26],"h2",{"id":25},"installing-flowfuse-user-addon","Installing Flowfuse user addon",[10,28,29,30,34],{},"The FlowFuse User Addon is a plugin developed for Dashboard 2.0, that levereges the FlowFuse API to access logged in user's information at Dashboard 2.0. For detailed information refer to the ",[17,31,33],{"href":32},"\u002Fblog\u002F2024\u002F04\u002Fdisplaying-logged-in-users-on-dashboard\u002F#exploring-the-flowfuse-user-addon","Exploring the FlowFuse User Addon"," and make sure to install it.",[10,36,37,38,42],{},"Before you begin the application development process, please make sure that FlowFuse user authentication is enabled. This feature adds a layer of security to your application with a login page. By combining the FlowFuse user addon with user authentication, we gain access to the logged in user's data within our application. For more information on FlowFuse user authentication, refer to the ",[17,39,41],{"href":40},"\u002Fdocs\u002Fuser\u002Finstance-settings\u002F#flowfuse-user-authentication","documentation"," and make sure that it is enabled.",[23,44,46],{"id":45},"building-task-management-application","Building Task Management application",[10,48,49],{},[50,51],"img",{"alt":52,"dataZoomable":53,"src":54,"title":55},"\"Screenshot of the Todo application built with Node-RED Dashboard 2.0\"","","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fbuilding-an-application-with-dashboard-2-task-management-system.gif","Screenshot of the task management system built with Node-RED Dashboard 2.0",[10,57,58],{},"Throughout this guide, we will be building a simple, secure, and personalized Task management application that will allow users to create and view their tasks.",[60,61,63],"h3",{"id":62},"building-a-form-to-submit-tasks","Building a Form to Submit Tasks",[65,66,67,76],"ol",{},[68,69,70,71,75],"li",{},"Drag an ",[72,73,74],"strong",{},"ui-form"," widget onto the canvas.",[68,77,78],{},"Click on the edit icon next to Page 1 (The default page added when you install Dashboard 2.0) in the Dashboard 2.0 sidebar. While this step is optional, updating the page configurations as shown in the image below ensures that your application aligns with the layout described in this guide.",[10,80,81],{},[50,82],{"alt":83,"dataZoomable":53,"src":84,"title":85},"\"Screenshot displaying 'new task' page configurations\"","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fbuilding-an-application-with-dashboard-2-page1-configuration.png","Screenshot displaying 'new task' page configurations",[65,87,89],{"start":88},3,[68,90,91,92,94],{},"Click on the ",[72,93,74],{}," widget to add form elements such as title, description, due date, and priority.",[10,96,97],{},[50,98],{"alt":99,"dataZoomable":53,"src":100,"title":101},"\"Screenshot of ui widget form configuration\"","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fbuilding-an-application-with-dashboard-2-task-submission-form.png","Screenshot of ui widget form configuration",[60,103,105],{"id":104},"storing-tasks-in-the-global-context","Storing Tasks in the Global Context",[10,107,108],{},"For this guide, we are storing our Tasks in Node-RED global context but storing them  in a database will make it easy to manage your task data.",[65,110,111,118],{},[68,112,113,114,117],{},"Drag a ",[72,115,116],{},"function"," node onto the canvas",[68,119,120,121,123],{},"Paste the below code in the ",[72,122,116],{}," node.",[125,126,130],"pre",{"className":127,"code":128,"language":129,"meta":53,"style":53},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F Retrieve the existing tasks from the global context or initialize an empty array if none exists\n\nlet tasks = global.get('tasks') || [];\n\n\u002F\u002F Push the new task object into the tasks array, including the task details and the user object extracted from the message object, as each payload emitted by the Node-RED Dashboard 2.0 widgets contains user information due to the FlowFuse User Addon.\n\ntasks.push({\n  ...msg.payload,\n  ...{\n    user: msg._client.user \u002F\u002F Assign the user object to the task\n  }\n});\n\n\u002F\u002F Update the 'tasks' variable in the global context with the modified tasks array\n\nglobal.set('tasks', tasks);\n\nreturn msg;\n","javascript",[131,132,133,142,149,185,190,196,201,213,222,230,239,245,251,256,262,267,283,288],"code",{"__ignoreMap":53},[134,135,138],"span",{"class":136,"line":137},"line",1,[134,139,141],{"class":140},"sJ8bj","\u002F\u002F Retrieve the existing tasks from the global context or initialize an empty array if none exists\n",[134,143,145],{"class":136,"line":144},2,[134,146,148],{"emptyLinePlaceholder":147},true,"\n",[134,150,151,155,159,162,165,169,172,176,179,182],{"class":136,"line":88},[134,152,154],{"class":153},"szBVR","let",[134,156,158],{"class":157},"sVt8B"," tasks ",[134,160,161],{"class":153},"=",[134,163,164],{"class":157}," global.",[134,166,168],{"class":167},"sScJk","get",[134,170,171],{"class":157},"(",[134,173,175],{"class":174},"sZZnC","'tasks'",[134,177,178],{"class":157},") ",[134,180,181],{"class":153},"||",[134,183,184],{"class":157}," [];\n",[134,186,188],{"class":136,"line":187},4,[134,189,148],{"emptyLinePlaceholder":147},[134,191,193],{"class":136,"line":192},5,[134,194,195],{"class":140},"\u002F\u002F Push the new task object into the tasks array, including the task details and the user object extracted from the message object, as each payload emitted by the Node-RED Dashboard 2.0 widgets contains user information due to the FlowFuse User Addon.\n",[134,197,199],{"class":136,"line":198},6,[134,200,148],{"emptyLinePlaceholder":147},[134,202,204,207,210],{"class":136,"line":203},7,[134,205,206],{"class":157},"tasks.",[134,208,209],{"class":167},"push",[134,211,212],{"class":157},"({\n",[134,214,216,219],{"class":136,"line":215},8,[134,217,218],{"class":153},"  ...",[134,220,221],{"class":157},"msg.payload,\n",[134,223,225,227],{"class":136,"line":224},9,[134,226,218],{"class":153},[134,228,229],{"class":157},"{\n",[134,231,233,236],{"class":136,"line":232},10,[134,234,235],{"class":157},"    user: msg._client.user ",[134,237,238],{"class":140},"\u002F\u002F Assign the user object to the task\n",[134,240,242],{"class":136,"line":241},11,[134,243,244],{"class":157},"  }\n",[134,246,248],{"class":136,"line":247},12,[134,249,250],{"class":157},"});\n",[134,252,254],{"class":136,"line":253},13,[134,255,148],{"emptyLinePlaceholder":147},[134,257,259],{"class":136,"line":258},14,[134,260,261],{"class":140},"\u002F\u002F Update the 'tasks' variable in the global context with the modified tasks array\n",[134,263,265],{"class":136,"line":264},15,[134,266,148],{"emptyLinePlaceholder":147},[134,268,270,273,276,278,280],{"class":136,"line":269},16,[134,271,272],{"class":157},"global.",[134,274,275],{"class":167},"set",[134,277,171],{"class":157},[134,279,175],{"class":174},[134,281,282],{"class":157},", tasks);\n",[134,284,286],{"class":136,"line":285},17,[134,287,148],{"emptyLinePlaceholder":147},[134,289,291,294],{"class":136,"line":290},18,[134,292,293],{"class":153},"return",[134,295,296],{"class":157}," msg;\n",[65,298,299],{"start":88},[68,300,301,302,304,305,307],{},"Connect the ",[72,303,74],{}," widget’s output to the ",[72,306,116],{}," node’s input.",[60,309,311],{"id":310},"displaying-notification-on-successful-task-submission","Displaying notification on successful task submission",[65,313,314],{},[68,315,113,316,319,320,323],{},[72,317,318],{},"change"," node onto the canvas and set ",[131,321,322],{},"msg.payload"," to the confirmation message you want to display on successful task submission.",[10,325,326],{},[50,327],{"alt":328,"dataZoomable":53,"src":329,"title":330},"\"screenshot of the change node setting payload for notification\"","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fbuilding-an-application-with-dashboard-change-node-setting-payload-for-notification.png","screenshot of the change node setting payload for notification",[65,332,333,343],{"start":144},[68,334,70,335,338,339,342],{},[72,336,337],{},"ui-notification"," onto the canvas select ",[72,340,341],{},"ui-base"," and set the position to \"center\".",[68,344,301,345,304,347,349,350,352,353,355],{},[72,346,74],{},[72,348,318],{}," node’s input and the ",[72,351,318],{}," node’s output to the ",[72,354,337],{}," widget's input.",[60,357,359],{"id":358},"retrieving-and-filtering-tasks","Retrieving and Filtering Tasks",[10,361,362],{},"Now that we can store tasks along with the user details of who submitted them, we need to retrieve and filter them based on users, ensuring that users can only see their tasks only and not others.",[65,364,365,377],{},[68,366,113,367,370,371,373,374,376],{},[72,368,369],{},"ui-event"," widget onto the canvas and select ",[72,372,341],{}," for it. The ",[72,375,369],{}," will enable us to display updated tasks on the table without the need for polling, as it triggers when the page reloads or changes.",[68,378,113,379,319,381,383,384,387],{},[72,380,318],{},[131,382,322],{}," to ",[131,385,386],{},"global.tasks",".",[10,389,390],{},[50,391],{"alt":392,"dataZoomable":53,"src":393,"title":394},"\"Screenshot of the change setting retriving global context and setting to msg.payload\"","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fbuilding-an-application-with-dashboard-change-node-retriving-global-context-data.png","Screenshot of the change setting retriving global context and setting to msg.payload",[65,396,397],{"start":88},[68,398,113,399,401],{},[72,400,116],{}," node onto the canvas and paste the below code into it.",[125,403,405],{"className":127,"code":404,"language":129,"meta":53,"style":53},"\u002F\u002F Filter the payload array of tasks to include only those tasks associated with the currently logged in user.\n\nmsg.payload = msg.payload.filter((task) => task.user.userId === msg._client.user.userId);\n\n\u002F\u002F Return the modified message object containing the filtered tasks.\n\nreturn msg;\n",[131,406,407,412,416,450,454,459,463],{"__ignoreMap":53},[134,408,409],{"class":136,"line":137},[134,410,411],{"class":140},"\u002F\u002F Filter the payload array of tasks to include only those tasks associated with the currently logged in user.\n",[134,413,414],{"class":136,"line":144},[134,415,148],{"emptyLinePlaceholder":147},[134,417,418,421,423,426,429,432,436,438,441,444,447],{"class":136,"line":88},[134,419,420],{"class":157},"msg.payload ",[134,422,161],{"class":153},[134,424,425],{"class":157}," msg.payload.",[134,427,428],{"class":167},"filter",[134,430,431],{"class":157},"((",[134,433,435],{"class":434},"s4XuR","task",[134,437,178],{"class":157},[134,439,440],{"class":153},"=>",[134,442,443],{"class":157}," task.user.userId ",[134,445,446],{"class":153},"===",[134,448,449],{"class":157}," msg._client.user.userId);\n",[134,451,452],{"class":136,"line":187},[134,453,148],{"emptyLinePlaceholder":147},[134,455,456],{"class":136,"line":192},[134,457,458],{"class":140},"\u002F\u002F Return the modified message object containing the filtered tasks.\n",[134,460,461],{"class":136,"line":198},[134,462,148],{"emptyLinePlaceholder":147},[134,464,465,467],{"class":136,"line":203},[134,466,293],{"class":153},[134,468,296],{"class":157},[65,470,471],{"start":187},[68,472,301,473,304,475,349,477,479,480,307],{},[72,474,369],{},[72,476,318],{},[72,478,318],{}," nodes’ output to the ",[72,481,116],{},[60,483,485],{"id":484},"enabling-client-constraint-for-ui-template","Enabling client constraint for ui-template",[10,487,488,489,492],{},"Before we begin building our table to display tasks, we need to enable access to client constraints for the ",[72,490,491],{},"ui-template"," widget. Access client constraints ensure that messages or actions are specifically targeted to individual clients. For instance, if 100 people are interacting with the same task management dashboard simultaneously and one person submits a task, the notification will only be visible to that person and not to the remaining 99 individuals.",[10,494,495,496,499,500,502],{},"If you have experience with Node-RED Dashboard 1.0, you may recall that these client constraints were only available for ",[72,497,498],{},"ui-control"," and ",[72,501,337],{}," widgets but in Dashboard 2.0 you can enable it for any widget.",[10,504,505],{},[50,506],{"alt":507,"dataZoomable":53,"src":508,"title":509},"\"Screenshot displaying FF Auth tab\"","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fbuilding-an-application-with-dashboard-2-ff-auth-tab.png","Screenshot displaying FF Auth tab",[65,511,512,515],{},[68,513,514],{},"Navigate to the Dashboard 2.0 sidebar and select the top-right \"FF Auth\" Tab",[68,516,517,518,499,520,522,523,525],{},"In the \"Accept Client Constraints\" option, you'll see Dashboard 2.0 widgets where this option is by default enabled for ",[72,519,337],{},[72,521,498],{},", enable it for ",[72,524,491],{}," as well.",[60,527,529],{"id":528},"creating-a-table-and-displaying-the-task","Creating a table and displaying the task",[10,531,532,533,499,535,541,542,544],{},"In this section, we will build an interactive table using ",[72,534,491],{},[17,536,540],{"href":537,"rel":538},"https:\u002F\u002Fvuetifyjs.com\u002Fen\u002Fcomponents\u002Fall\u002F",[539],"nofollow","vuetify component",". Vuetify offers a wide range of components, all of which are compatible with our Node-RED Dashboard 2.0's ui-template widget. You can easily use them by just simply copying and pasting them into the ",[72,543,491],{}," widget.",[65,546,547,552],{},[68,548,70,549,551],{},[72,550,491],{}," widget onto the canvas",[68,553,554,555,499,558,561],{},"Create a new ",[72,556,557],{},"ui-page",[72,559,560],{},"ui-group"," for it. Below, I have provided a screenshot of the \"new task\" page configurations. Again You can replicate it if you want to align with the layout described in this guide, otherwise, it is optional.",[10,563,564],{},[50,565],{"alt":566,"dataZoomable":53,"src":567,"title":568},"\"Screenshot displaying ui-template widget with code for building table for displaying task\"","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fbuilding-an-application-with-dashboard-template-widget.png","Screenshot displaying ui-template widget with code for building table for displaying task",[65,570,571],{"start":88},[68,572,573],{},"Paste the below code into the widget, If you're new to Vue.js, rest assured I've included helpful comments for clarity.",[125,575,579],{"className":576,"code":577,"language":578,"meta":53,"style":53},"language-html shiki shiki-themes github-light github-dark","` \u003Ctemplate>\n \u003C!-- Input field for searching tasks -->\n \u003Cv-text-field v-model=\"search\" label=\"Search\" prepend-inner-icon=\"mdi-magnify\" single-line variant=\"outlined\"\n  hide-details>\u003C\u002Fv-text-field>\n \n \u003C!-- Data table to display tasks -->\n \u003Cv-data-table :search=\"search\" :items=\"msg?.payload\">\n  \u003C!-- Custom header for the \"current\" column -->\n  \u003Ctemplate v-slot:header.current>\n   \u003Cdiv class=\"text-center\">Center-Aligned\u003C\u002Fdiv>\n  \u003C\u002Ftemplate>\n\n  \u003C!-- Template for the \"priority\" column -->\n  \u003Ctemplate v-slot:item.priority=\"{ item }\">\n   \u003C!-- Display priority icon if it exists -->\n   \u003Cv-icon v-if=\"item.priority\" icon=\"mdi-alert\" color=\"red\">\u003C\u002Fv-icon>\n  \u003C\u002Ftemplate>\n\n  \u003C!-- Template for the \"user\" column -->\n  \u003Ctemplate v-slot:item.user=\"{ item }\">\n   \u003C!-- Display user avatar and username -->\n   \u003Cdiv class=\"user\">\n    \u003C!-- User avatar -->\n    \u003Cimg :src=\"item.user.image\" width=\"24\" \u002F>\n    \u003C!-- Username -->\n    \u003Cspan>{{ item.user.username }}\u003C\u002Fspan>\n   \u003C\u002Fdiv>\n  \u003C\u002Ftemplate>\n\n  \u003C!-- Template for the \"due\" column -->\n  \u003Ctemplate v-slot:item.due=\"{ item }\">\n   \u003C!-- Calculate and display the number of days between due date and current date -->\n   {{ daysBetween(item.due, new Date()) }} Days\n  \u003C\u002Ftemplate>\n \u003C\u002Fv-data-table>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\n export default {\n  data() {\n   return {\n    \u002F\u002F Search input field model\n    search: '',\n   }\n  },\n  methods: {\n   \u002F\u002F Method to calculate the number of days between two dates\n   daysBetween(date1, date2) {\n    \u002F\u002F Calculate the difference in days\n    const oneDay = 24 * 60 * 60 * 1000; \u002F\u002F hours*minutes*seconds*milliseconds\n    const firstDate = new Date(date1);\n    const secondDate = new Date(date2);\n    const diffDays = Math.round(Math.abs((firstDate - secondDate) \u002F oneDay));\n    return diffDays;\n   }\n  }\n }\n\u003C\u002Fscript>\n\n\u003Cstyle scoped>\n \u002F* Styling for user avatar and username *\u002F\n .user {\n  display: flex;\n  gap: 5px; \u002F* Gap between avatar and username *\u002F\n }\n\n \u002F* Styling for user avatar *\u002F\n .user img {\n  width: 24px; \u002F* Set width of user avatar *\u002F\n }\n\u003C\u002Fstyle>\n\n","html",[131,580,581,593,598,641,653,658,663,687,692,704,727,736,740,745,761,766,803,811,815,821,837,843,859,865,892,898,912,922,931,936,942,958,964,970,979,989,999,1004,1015,1027,1036,1044,1050,1062,1068,1074,1080,1086,1106,1112,1149,1168,1185,1222,1231,1236,1241,1247,1256,1261,1274,1280,1288,1303,1322,1327,1332,1338,1348,1366,1371],{"__ignoreMap":53},[134,582,583,586,590],{"class":136,"line":137},[134,584,585],{"class":157},"` \u003C",[134,587,589],{"class":588},"s9eBZ","template",[134,591,592],{"class":157},">\n",[134,594,595],{"class":136,"line":144},[134,596,597],{"class":140}," \u003C!-- Input field for searching tasks -->\n",[134,599,600,603,606,609,611,614,617,619,622,625,627,630,633,636,638],{"class":136,"line":88},[134,601,602],{"class":157}," \u003C",[134,604,605],{"class":588},"v-text-field",[134,607,608],{"class":167}," v-model",[134,610,161],{"class":157},[134,612,613],{"class":174},"\"search\"",[134,615,616],{"class":167}," label",[134,618,161],{"class":157},[134,620,621],{"class":174},"\"Search\"",[134,623,624],{"class":167}," prepend-inner-icon",[134,626,161],{"class":157},[134,628,629],{"class":174},"\"mdi-magnify\"",[134,631,632],{"class":167}," single-line",[134,634,635],{"class":167}," variant",[134,637,161],{"class":157},[134,639,640],{"class":174},"\"outlined\"\n",[134,642,643,646,649,651],{"class":136,"line":187},[134,644,645],{"class":167},"  hide-details",[134,647,648],{"class":157},">\u003C\u002F",[134,650,605],{"class":588},[134,652,592],{"class":157},[134,654,655],{"class":136,"line":192},[134,656,657],{"class":157}," \n",[134,659,660],{"class":136,"line":198},[134,661,662],{"class":140}," \u003C!-- Data table to display tasks -->\n",[134,664,665,667,670,673,675,677,680,682,685],{"class":136,"line":203},[134,666,602],{"class":157},[134,668,669],{"class":588},"v-data-table",[134,671,672],{"class":167}," :search",[134,674,161],{"class":157},[134,676,613],{"class":174},[134,678,679],{"class":167}," :items",[134,681,161],{"class":157},[134,683,684],{"class":174},"\"msg?.payload\"",[134,686,592],{"class":157},[134,688,689],{"class":136,"line":215},[134,690,691],{"class":140},"  \u003C!-- Custom header for the \"current\" column -->\n",[134,693,694,697,699,702],{"class":136,"line":224},[134,695,696],{"class":157},"  \u003C",[134,698,589],{"class":588},[134,700,701],{"class":167}," v-slot:header.current",[134,703,592],{"class":157},[134,705,706,709,712,715,717,720,723,725],{"class":136,"line":232},[134,707,708],{"class":157},"   \u003C",[134,710,711],{"class":588},"div",[134,713,714],{"class":167}," class",[134,716,161],{"class":157},[134,718,719],{"class":174},"\"text-center\"",[134,721,722],{"class":157},">Center-Aligned\u003C\u002F",[134,724,711],{"class":588},[134,726,592],{"class":157},[134,728,729,732,734],{"class":136,"line":241},[134,730,731],{"class":157},"  \u003C\u002F",[134,733,589],{"class":588},[134,735,592],{"class":157},[134,737,738],{"class":136,"line":247},[134,739,148],{"emptyLinePlaceholder":147},[134,741,742],{"class":136,"line":253},[134,743,744],{"class":140},"  \u003C!-- Template for the \"priority\" column -->\n",[134,746,747,749,751,754,756,759],{"class":136,"line":258},[134,748,696],{"class":157},[134,750,589],{"class":588},[134,752,753],{"class":167}," v-slot:item.priority",[134,755,161],{"class":157},[134,757,758],{"class":174},"\"{ item }\"",[134,760,592],{"class":157},[134,762,763],{"class":136,"line":264},[134,764,765],{"class":140},"   \u003C!-- Display priority icon if it exists -->\n",[134,767,768,770,773,776,778,781,784,786,789,792,794,797,799,801],{"class":136,"line":269},[134,769,708],{"class":157},[134,771,772],{"class":588},"v-icon",[134,774,775],{"class":167}," v-if",[134,777,161],{"class":157},[134,779,780],{"class":174},"\"item.priority\"",[134,782,783],{"class":167}," icon",[134,785,161],{"class":157},[134,787,788],{"class":174},"\"mdi-alert\"",[134,790,791],{"class":167}," color",[134,793,161],{"class":157},[134,795,796],{"class":174},"\"red\"",[134,798,648],{"class":157},[134,800,772],{"class":588},[134,802,592],{"class":157},[134,804,805,807,809],{"class":136,"line":285},[134,806,731],{"class":157},[134,808,589],{"class":588},[134,810,592],{"class":157},[134,812,813],{"class":136,"line":290},[134,814,148],{"emptyLinePlaceholder":147},[134,816,818],{"class":136,"line":817},19,[134,819,820],{"class":140},"  \u003C!-- Template for the \"user\" column -->\n",[134,822,824,826,828,831,833,835],{"class":136,"line":823},20,[134,825,696],{"class":157},[134,827,589],{"class":588},[134,829,830],{"class":167}," v-slot:item.user",[134,832,161],{"class":157},[134,834,758],{"class":174},[134,836,592],{"class":157},[134,838,840],{"class":136,"line":839},21,[134,841,842],{"class":140},"   \u003C!-- Display user avatar and username -->\n",[134,844,846,848,850,852,854,857],{"class":136,"line":845},22,[134,847,708],{"class":157},[134,849,711],{"class":588},[134,851,714],{"class":167},[134,853,161],{"class":157},[134,855,856],{"class":174},"\"user\"",[134,858,592],{"class":157},[134,860,862],{"class":136,"line":861},23,[134,863,864],{"class":140},"    \u003C!-- User avatar -->\n",[134,866,868,871,873,876,878,881,884,886,889],{"class":136,"line":867},24,[134,869,870],{"class":157},"    \u003C",[134,872,50],{"class":588},[134,874,875],{"class":167}," :src",[134,877,161],{"class":157},[134,879,880],{"class":174},"\"item.user.image\"",[134,882,883],{"class":167}," width",[134,885,161],{"class":157},[134,887,888],{"class":174},"\"24\"",[134,890,891],{"class":157}," \u002F>\n",[134,893,895],{"class":136,"line":894},25,[134,896,897],{"class":140},"    \u003C!-- Username -->\n",[134,899,901,903,905,908,910],{"class":136,"line":900},26,[134,902,870],{"class":157},[134,904,134],{"class":588},[134,906,907],{"class":157},">{{ item.user.username }}\u003C\u002F",[134,909,134],{"class":588},[134,911,592],{"class":157},[134,913,915,918,920],{"class":136,"line":914},27,[134,916,917],{"class":157},"   \u003C\u002F",[134,919,711],{"class":588},[134,921,592],{"class":157},[134,923,925,927,929],{"class":136,"line":924},28,[134,926,731],{"class":157},[134,928,589],{"class":588},[134,930,592],{"class":157},[134,932,934],{"class":136,"line":933},29,[134,935,148],{"emptyLinePlaceholder":147},[134,937,939],{"class":136,"line":938},30,[134,940,941],{"class":140},"  \u003C!-- Template for the \"due\" column -->\n",[134,943,945,947,949,952,954,956],{"class":136,"line":944},31,[134,946,696],{"class":157},[134,948,589],{"class":588},[134,950,951],{"class":167}," v-slot:item.due",[134,953,161],{"class":157},[134,955,758],{"class":174},[134,957,592],{"class":157},[134,959,961],{"class":136,"line":960},32,[134,962,963],{"class":140},"   \u003C!-- Calculate and display the number of days between due date and current date -->\n",[134,965,967],{"class":136,"line":966},33,[134,968,969],{"class":157},"   {{ daysBetween(item.due, new Date()) }} Days\n",[134,971,973,975,977],{"class":136,"line":972},34,[134,974,731],{"class":157},[134,976,589],{"class":588},[134,978,592],{"class":157},[134,980,982,985,987],{"class":136,"line":981},35,[134,983,984],{"class":157}," \u003C\u002F",[134,986,669],{"class":588},[134,988,592],{"class":157},[134,990,992,995,997],{"class":136,"line":991},36,[134,993,994],{"class":157},"\u003C\u002F",[134,996,589],{"class":588},[134,998,592],{"class":157},[134,1000,1002],{"class":136,"line":1001},37,[134,1003,148],{"emptyLinePlaceholder":147},[134,1005,1007,1010,1013],{"class":136,"line":1006},38,[134,1008,1009],{"class":157},"\u003C",[134,1011,1012],{"class":588},"script",[134,1014,592],{"class":157},[134,1016,1018,1021,1024],{"class":136,"line":1017},39,[134,1019,1020],{"class":153}," export",[134,1022,1023],{"class":153}," default",[134,1025,1026],{"class":157}," {\n",[134,1028,1030,1033],{"class":136,"line":1029},40,[134,1031,1032],{"class":167},"  data",[134,1034,1035],{"class":157},"() {\n",[134,1037,1039,1042],{"class":136,"line":1038},41,[134,1040,1041],{"class":153},"   return",[134,1043,1026],{"class":157},[134,1045,1047],{"class":136,"line":1046},42,[134,1048,1049],{"class":140},"    \u002F\u002F Search input field model\n",[134,1051,1053,1056,1059],{"class":136,"line":1052},43,[134,1054,1055],{"class":157},"    search: ",[134,1057,1058],{"class":174},"''",[134,1060,1061],{"class":157},",\n",[134,1063,1065],{"class":136,"line":1064},44,[134,1066,1067],{"class":157},"   }\n",[134,1069,1071],{"class":136,"line":1070},45,[134,1072,1073],{"class":157},"  },\n",[134,1075,1077],{"class":136,"line":1076},46,[134,1078,1079],{"class":157},"  methods: {\n",[134,1081,1083],{"class":136,"line":1082},47,[134,1084,1085],{"class":140},"   \u002F\u002F Method to calculate the number of days between two dates\n",[134,1087,1089,1092,1094,1097,1100,1103],{"class":136,"line":1088},48,[134,1090,1091],{"class":167},"   daysBetween",[134,1093,171],{"class":157},[134,1095,1096],{"class":434},"date1",[134,1098,1099],{"class":157},", ",[134,1101,1102],{"class":434},"date2",[134,1104,1105],{"class":157},") {\n",[134,1107,1109],{"class":136,"line":1108},49,[134,1110,1111],{"class":140},"    \u002F\u002F Calculate the difference in days\n",[134,1113,1115,1118,1122,1125,1128,1131,1134,1136,1138,1140,1143,1146],{"class":136,"line":1114},50,[134,1116,1117],{"class":153},"    const",[134,1119,1121],{"class":1120},"sj4cs"," oneDay",[134,1123,1124],{"class":153}," =",[134,1126,1127],{"class":1120}," 24",[134,1129,1130],{"class":153}," *",[134,1132,1133],{"class":1120}," 60",[134,1135,1130],{"class":153},[134,1137,1133],{"class":1120},[134,1139,1130],{"class":153},[134,1141,1142],{"class":1120}," 1000",[134,1144,1145],{"class":157},"; ",[134,1147,1148],{"class":140},"\u002F\u002F hours*minutes*seconds*milliseconds\n",[134,1150,1152,1154,1157,1159,1162,1165],{"class":136,"line":1151},51,[134,1153,1117],{"class":153},[134,1155,1156],{"class":1120}," firstDate",[134,1158,1124],{"class":153},[134,1160,1161],{"class":153}," new",[134,1163,1164],{"class":167}," Date",[134,1166,1167],{"class":157},"(date1);\n",[134,1169,1171,1173,1176,1178,1180,1182],{"class":136,"line":1170},52,[134,1172,1117],{"class":153},[134,1174,1175],{"class":1120}," secondDate",[134,1177,1124],{"class":153},[134,1179,1161],{"class":153},[134,1181,1164],{"class":167},[134,1183,1184],{"class":157},"(date2);\n",[134,1186,1188,1190,1193,1195,1198,1201,1204,1207,1210,1213,1216,1219],{"class":136,"line":1187},53,[134,1189,1117],{"class":153},[134,1191,1192],{"class":1120}," diffDays",[134,1194,1124],{"class":153},[134,1196,1197],{"class":157}," Math.",[134,1199,1200],{"class":167},"round",[134,1202,1203],{"class":157},"(Math.",[134,1205,1206],{"class":167},"abs",[134,1208,1209],{"class":157},"((firstDate ",[134,1211,1212],{"class":153},"-",[134,1214,1215],{"class":157}," secondDate) ",[134,1217,1218],{"class":153},"\u002F",[134,1220,1221],{"class":157}," oneDay));\n",[134,1223,1225,1228],{"class":136,"line":1224},54,[134,1226,1227],{"class":153},"    return",[134,1229,1230],{"class":157}," diffDays;\n",[134,1232,1234],{"class":136,"line":1233},55,[134,1235,1067],{"class":157},[134,1237,1239],{"class":136,"line":1238},56,[134,1240,244],{"class":157},[134,1242,1244],{"class":136,"line":1243},57,[134,1245,1246],{"class":157}," }\n",[134,1248,1250,1252,1254],{"class":136,"line":1249},58,[134,1251,994],{"class":157},[134,1253,1012],{"class":588},[134,1255,592],{"class":157},[134,1257,1259],{"class":136,"line":1258},59,[134,1260,148],{"emptyLinePlaceholder":147},[134,1262,1264,1266,1269,1272],{"class":136,"line":1263},60,[134,1265,1009],{"class":157},[134,1267,1268],{"class":588},"style",[134,1270,1271],{"class":167}," scoped",[134,1273,592],{"class":157},[134,1275,1277],{"class":136,"line":1276},61,[134,1278,1279],{"class":157}," \u002F* Styling for user avatar and username *\u002F\n",[134,1281,1283,1286],{"class":136,"line":1282},62,[134,1284,1285],{"class":167}," .user",[134,1287,1026],{"class":157},[134,1289,1291,1294,1297,1300],{"class":136,"line":1290},63,[134,1292,1293],{"class":1120},"  display",[134,1295,1296],{"class":157},": ",[134,1298,1299],{"class":1120},"flex",[134,1301,1302],{"class":157},";\n",[134,1304,1306,1309,1311,1314,1317,1319],{"class":136,"line":1305},64,[134,1307,1308],{"class":1120},"  gap",[134,1310,1296],{"class":157},[134,1312,1313],{"class":1120},"5",[134,1315,1316],{"class":153},"px",[134,1318,1145],{"class":157},[134,1320,1321],{"class":140},"\u002F* Gap between avatar and username *\u002F\n",[134,1323,1325],{"class":136,"line":1324},65,[134,1326,1246],{"class":157},[134,1328,1330],{"class":136,"line":1329},66,[134,1331,148],{"emptyLinePlaceholder":147},[134,1333,1335],{"class":136,"line":1334},67,[134,1336,1337],{"class":157}," \u002F* Styling for user avatar *\u002F\n",[134,1339,1341,1343,1346],{"class":136,"line":1340},68,[134,1342,1285],{"class":167},[134,1344,1345],{"class":588}," img",[134,1347,1026],{"class":157},[134,1349,1351,1354,1356,1359,1361,1363],{"class":136,"line":1350},69,[134,1352,1353],{"class":1120},"  width",[134,1355,1296],{"class":157},[134,1357,1358],{"class":1120},"24",[134,1360,1316],{"class":153},[134,1362,1145],{"class":157},[134,1364,1365],{"class":140},"\u002F* Set width of user avatar *\u002F\n",[134,1367,1369],{"class":136,"line":1368},70,[134,1370,1246],{"class":157},[134,1372,1374,1376,1378],{"class":136,"line":1373},71,[134,1375,994],{"class":157},[134,1377,1268],{"class":588},[134,1379,592],{"class":157},[65,1381,1382],{"start":88},[68,1383,301,1384,1386,1387,1389],{},[72,1385,491],{}," widget's input to the ",[72,1388,116],{}," node's output ( function node which we have added to filter tasks based on user ).",[23,1391,1393],{"id":1392},"deploying-the-flow","Deploying the flow",[10,1395,1396],{},[50,1397],{"alt":1398,"dataZoomable":53,"src":1399,"title":1400},"\"Screenshot displaying Node-RED flow of Task management system\"","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fbuilding-an-application-with-dashboard-task-management-application-flow.png","Screenshot displaying Node-RED flow of Task management system",[65,1402,1403,1406,1409],{},[68,1404,1405],{},"Deploy the flow by clicking the top right Deploy button.",[68,1407,1408],{},"Locate the 'Open Dashboard' button at the top-right corner of the Dashboard 2.0 sidebar and click on it to navigate to the dashboard.",[68,1410,1411],{},"Login with the registered FlowFuse account username and password.",[10,1413,1414],{},"Now, we're all set to add tasks. Navigate to the \"New Task\" page to add tasks. To view tasks, navigate to the \"your Task\" page.",[23,1416,1418],{"id":1417},"next-step","Next step",[10,1420,1421],{},"If you want to enhance this simple application by adding more features, consider the following resources:",[1423,1424,1425,1432,1439,1446],"ul",{},[68,1426,1427,1431],{},[17,1428,1430],{"href":1429},"\u002Fwebinars\u002F2024\u002Fnode-red-dashboard-multi-user\u002F","Webinar"," - This webinar provides an in-depth discussion of the Personalised Multi-User Dashboards feature and offers guidance on how to get started with it.",[68,1433,1434,1438],{},[17,1435,1437],{"href":1436},"\u002Fblog\u002F2024\u002F04\u002Fdisplaying-logged-in-users-on-dashboard\u002F","Displaying logged-in users on Dashboard 2.0"," - This detailed guide demonstrates how to display logged-in users on Dashboard 2.0 which using the FlowFuse Multiuser addon and FlowFuse.",[68,1440,1441,1445],{},[17,1442,1444],{"href":1443},"\u002Fblog\u002F2024\u002F04\u002Fbuilding-an-admin-panel-in-node-red-with-dashboard-2\u002F","How to Build an Admin Dashboard with Node-RED Dashboard 2.0"," - This detailed guide demonstrates how to build a secure admin page in Node-RED Dashboard 2.0.",[68,1447,1448,1452],{},[17,1449,1451],{"href":1450},"\u002Fblueprints\u002Fflowfuse-dashboard\u002Fmulti-user-dashboard\u002F","Multi-User Dashboard for Ticket\u002FTask Management"," blueprint, which allows you to utilize templates to develop a personalized multi-user dashboard quickly. This Task management blueprint has all features such as adding, updating, and deleting tasks, user profiles, and admin dashboard.",[1268,1454,1455],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":53,"searchDepth":144,"depth":144,"links":1457},[1458,1459,1467,1468],{"id":25,"depth":144,"text":26},{"id":45,"depth":144,"text":46,"children":1460},[1461,1462,1463,1464,1465,1466],{"id":62,"depth":88,"text":63},{"id":104,"depth":88,"text":105},{"id":310,"depth":88,"text":311},{"id":358,"depth":88,"text":359},{"id":484,"depth":88,"text":485},{"id":528,"depth":88,"text":529},{"id":1392,"depth":144,"text":1393},{"id":1417,"depth":144,"text":1418},"md",{"navTitle":5,"excerpt":1471},{"type":7,"value":1472},[1473],[10,1474,12],{},"\u002Fblog\u002F2024\u002F04\u002Fhow-to-build-an-application-with-node-red-dashboard-2",{"title":5,"description":12},"blog\u002F2024\u002F04\u002Fhow-to-build-an-application-with-node-red-dashboard-2","sc6u2NpVt1s-S1DXsPGCw7blJn9qZPWaYm3NnVxmFxw",1780070551702]