In this project, I developed a Canvas App using Microsoft Power Apps tailored for mobile users. The objective is to enhance the process of identifying and registering items requiring maintenance or cleaning. Users can log in to the app, check if they have already inspected all the gym rooms, and if any item needs maintenance/cleaning, they can take pictures with the app and register the issue. All the necessary data is stored in Microsoft Dataverse.
José do Egipto Ferreira Antunes, from Braga, Portugal.
In this document, I will outline the main features and relevant code utilized. For more detailed information, you can access the “Documentation” subfolder within this directory.
To log in to the app, the user needs to be registered in the Users table on Dataverse, which should include a Username and a Password. This page also provides navigation to the password recovery page.
Below is the code for the credentials check:
If(LookUp(UsersInfo;Username=Username;Username)=Username.Text
&&
LookUp(UsersInfo;Username=Username;Password)=Password.Text;
Navigate(LoginSucess)
&&
Set(varUsername;Username.Text);
Reset(Username)&&Reset(Password));;
When the Username exist on the Users table it turns green, due to this code in the text color property:
If(IsBlank(Username.Text); RGBA(166; 166; 166; 1);
If(LookUp(UsersInfo; Username = Username; Username) = Username.Text;
RGBA(0; 255; 0; 1)))
The Password is hidden by default, but pressing the blue button changes it to visible using this code in the Mode property of the text input:
If(BotaoVer.Pressed; TextMode.SingleLine; TextMode.Password)
I also set the Username and Password to be reset each time this page is visible.
At the start of the app, I define the refresh of the Users table information and a variable to check the version of the app. I used this code:
Refresh(UsersInfo);;
ClearCollect(Usernames; UsersInfo);;
Set(varAppVersion; "Version "& CountRows(
PowerAppsparaCriadores.GetAppVersions(
First(
Filter(
PowerAppsparaCriadores.GetApps().value;
properties.displayName = "AppName"
)
).name
).value
)
& ".0.0.0");;
On this page, it welcomes the user (using the varUsername created at login), displays the date, and provides an option to change the account (navigate to the login page) and a right arrow to navigate to the home page.
In the home page, the user can select navigation for three pages: the gym maintenance page, the change password page, and the recover password page.
The home page, as well as most of the pages of the app, were designed using containers (either horizontal or vertical). Typically, they consist of at least three containers: the header, the footer, and the body. The body contains inner containers as needed. Components were created for the header and footer since they remain consistent across most pages.
For the header component, I created a horizontal container and added three icons. They facilitate navigation to the home page, going back, and navigating to the login page. To make navigation work, I created three custom properties of the screen type within the component, one for each icon. On each icon, I added the code componentName.customProperty() to the OnSelect property of the button. Additionally, I included App.ActiveScreen in each custom property. Finally, to enable navigation within the app, I added the corresponding code to the custom properties on the screen to navigate to the desired page.
It’s important to note that some pages have all three icons in the header, but not all icons work for security reasons. For instance, on the recover password page, only the X icon (to navigate to the login page) functions. This is because users on the login page can navigate to the recover password page without logging in. If the home icon worked, users could access the app without logging in, which poses a security risk.
The footer component consists of a single vertical container, which presents the version of the app using the variable “varAppVersion” created at the app’s start (check the code above).
When the user clicks the Gym Management button on the home page, they are redirected to this page, where the different rooms of the gym are presented. The user can select which room they want to inspect. The room buttons are displayed as a gallery, which is fed with info from a table in Dataverse. This table contains the gym items to be inspected (such as gym machines), the room to which each item belongs, and whether the item needs to be photographed to be considered verified (see below). To display the rooms without repetition, the “Distinct” function is used in the Items gallery property (Distinct(‘GymTable’; Room)).
When the user presses the button for the desired room, the app navigates to the Items page and creates a variable that stores the corresponding room (varRoom).
On this page, a gallery is presented with the items in the GymTable. The items are filtered using the varRoom created on the previous screen (Filter(‘GymTable’, Room=varRoom)). An icon of a camera is included only for items in the table that are stated as needing photographs to confirm their condition verification. The icon also leads to the navigation to the camera page to take pictures of that specific item when pressed. The color of the icon changes from blue to green when the item already has pictures saved on that day. The code used was:
//Code in OnSelect Property - For the navigation to camera page:
Navigate(Camera);;
Set(varItem; ThisItem);;
//Code in Visible Property - to appear the icon only in the desired items:
If(ThisItem.temFoto=0; true; false)
//Code in Color Property - To change Icon color if has pictures on that day:
If(IsBlank(
LookUp(FotosSalvas;Item = ThisItem.ItemsVerificar&&Sala=varSala&&Data=Today()).Item);
RGBA(0; 84; 148; 1);
Color.DarkGreen)
The toggles in each item are for the user to indicate that they have already checked the condition of that item. After a session of verifications, the user can register the verification by pressing the button at the bottom. That button patches the data (the item’s name and room, the status of the toggle, and the date) to another table on Dataverse, the table of inspections performed. Here is the code:
ForAll(Gallery6.AllItems;
Patch(ItemsInspecionados;
Defaults(ItemsInspecionados);
{
Sala: varItem.Local;
Data: Today();
Item: Title6.Text;
EstadoToggle: Toggle1.Value
}
)
)
This page allows the user to open the camera of their smartphone (or other device) and take pictures of the items for inspection. At the top of the body container, there is a square to display the camera view. In the middle, there are icons to press to take pictures, change the camera, and navigate to the gallery page. Below, it presents a snapshot of the last picture taken.
When the icon to take a picture is pressed, it also sends the picture to a collection. Here is some relevant code:
//OnSelect property of take picture Icon:
UpdateContext({varImagem: Camera1.Stream});;
Collect(Galeria; Camera1.Stream);;
//OnSelect property of change camera Icon:
If(varCamera>=CountRows(Camera1.AvailableDevices);
UpdateContext({varCamera:0}); UpdateContext({varCamera: varCamera + 1})
);;
//OnSelect property of navigate to gallery Icon:
Navigate(GymGallery)
This page presents the pictures taken in that app session. It features a Gallery component, allowing scrolling left and right to view all the pictures. Users can also save or delete each picture, and when saving, they can add some observations about the item.
The picture, observations, date, room name, and item name are stored in another Dataverse table (FotosSalvas) using the Patch function:
Patch(FotosSalvas;
Defaults(FotosSalvas);
{
Sala: varItem.Local;
Data: Today();
Imagem: {Full: Image5.Image; Value: Image5.Image};
Item: varItem.ItemsVerificar;
Observacoes: txt_obs.Text
}
)
This page is solely for recovering a user’s password. It prompts the user for their username and then sends the password to the email associated with the user’s account in the Dataverse table. Additionally, it provides on-screen notifications regarding the non-existence of a user or the success of the operation. Below is the code executed when the button is pressed:
If(
IsBlank(
LookUp(
UsersInfo;
Username = YourUsername.Text));
Notify(
"Nome de usuário não encontrado. Por favor, verifique e tente novamente.";
NotificationType.Error
) && Reset(YourUsername);
Office365Outlook.SendEmailV2(LookUp(UsersInfo; Username = YourUsername.Text; Email);
"Recuperação de password";
"Olá, segue a tua password do sistema de manutenção do ginásio: " & LookUp(UsersInfo; Username = YourUsername.Text; Password))
&&
Notify(
"Senha enviada para: "& LookUp(UsersInfo; Username = YourUsername.Text; Email);
NotificationType.Success
)&&
Navigate(Login;ScreenTransition.Fade);;);;
Finally, the app also includes a page for changing the password. It prompts the user for their username, current password, and desired new password. When the button is pressed, the software checks if the current password matches the username. If it does, it changes the password to the new one provided. The app also provides on-screen notifications if the username or password is incorrect, as well as if the operation was successful. Below is the code inserted into the OnSelect property of the button:
If(
IsBlank(LookUp(
UsersInfo;
Username = Username_reset.Text && Password = OldPassword.Text
));
Notify("Nome de usuário não encontrado ou senha incorreta."; NotificationType.Error)
&&
Reset(Username_reset)
&&
Reset(NewPassword)
;
UpdateIf(
UsersInfo;
Username = Username_reset.Text && Password = OldPassword.Text;
{Password: NewPassword.Text})
;;
Notify("Senha redefinida com sucesso."; NotificationType.Success)
&&
Navigate(Login; ScreenTransition.Fade)
);;
Here is a comprehensive overview of the navigation options available to the user within the app:
Navigation Options
These navigation options facilitate seamless interaction with the app, ensuring users can easily access the features they need.
Anyone can use, copy, modify, merge, publish, and distribute all the software, provided that: