Compare commits

...

23 Commits

Author SHA1 Message Date
Florian
7f64366a78 reflection 2025-01-22 14:52:29 +01:00
Xaver
06fa75ecc0 xaver feedback 2025-01-22 13:30:25 +01:00
Xaver
46b5ab236c Merge remote-tracking branch 'origin/Florian' 2025-01-22 12:55:22 +01:00
Xaver
d4b52e9720 . 2025-01-22 12:51:11 +01:00
Xaver
5b9b538a9a report 2025-01-22 12:48:40 +01:00
Xaver
a1b030afdc fixed images 2025-01-22 12:37:50 +01:00
Xaver
797a7fecb0 . 2025-01-22 12:34:19 +01:00
Xaver
d3f11f4d12 . 2025-01-22 11:49:58 +01:00
Xaver
4b80a3a78a . 2025-01-22 11:49:31 +01:00
Xaver
21061a26bf . 2025-01-22 11:45:58 +01:00
Xaver
3cdb3a0ae7 . 2025-01-22 11:33:16 +01:00
Xaver
f720a7ba30 readme report 2025-01-20 13:21:19 +01:00
Xaver
09ff5c2016 fixed no error bug 2025-01-20 12:56:15 +01:00
Xaver
678ce92b5e , 2025-01-20 12:22:18 +01:00
Xaver
c799b0e8a8 . 2025-01-20 10:15:40 +01:00
Florian
69d36abcad Merge remote-tracking branch 'origin/main' 2025-01-20 10:13:40 +01:00
Florian
2da0e0ac40 Merge remote-tracking branch 'origin/Florian'
# Conflicts:
#	Ledger/app/src/main/java/at/xaxa/ledger/ui/category/add/AddCategoryUI.kt
#	Ledger/app/src/main/java/at/xaxa/ledger/ui/entry/add/AddUI.kt
#	Ledger/app/src/main/java/at/xaxa/ledger/ui/entry/edit/EditUI.kt
2025-01-20 10:13:26 +01:00
Xaver
8262569134 fixed redir 2025-01-20 10:11:45 +01:00
Xaver
2bb80953c8 icon when selecting 2025-01-20 09:40:50 +01:00
Xaver
0f271451ac readme 2025-01-17 15:55:38 +01:00
Xaver
2465d2c6ab readme 2025-01-17 15:37:34 +01:00
Xaver
b87db2d95d Merge remote-tracking branch 'origin/Florian' 2025-01-17 15:33:31 +01:00
Xaver
ef5392bb3a . 2025-01-17 15:21:10 +01:00
17 changed files with 340 additions and 34 deletions

View File

@ -14,11 +14,14 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.DateRange import androidx.compose.material.icons.filled.DateRange
import androidx.compose.material.icons.filled.List import androidx.compose.material.icons.filled.List
import androidx.compose.material3.Button import androidx.compose.material3.Button
@ -29,6 +32,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
@ -80,11 +84,17 @@ fun HeaderCard(modifier: Modifier = Modifier, balance: String, onCategoryButton:
.padding(16.dp) .padding(16.dp)
) { ) {
Column() { Column() {
IconButton(onClick = { OutlinedButton(
modifier= Modifier.size(50.dp),
shape = CircleShape,
border= BorderStroke(1.dp, Color(0xffc6c6c6)),
contentPadding = PaddingValues(0.dp),
onClick = {
onCategoryButton() onCategoryButton()
}) { }
) {
Icon( Icon(
imageVector = Icons.Filled.List, imageVector = Icons.Filled.Add,
contentDescription = "Add to list", // Provide a meaningful description for accessibility contentDescription = "Add to list", // Provide a meaningful description for accessibility
tint = MaterialTheme.colorScheme.onSurface // Optional: Adjust the tint as needed tint = MaterialTheme.colorScheme.onSurface // Optional: Adjust the tint as needed
) )
@ -222,6 +232,7 @@ fun LayoutMediaText(modifier: Modifier = Modifier, name: String, date: String, a
} }
Box( Box(
modifier = Modifier modifier = Modifier
.weight(1f)
.fillMaxHeight() .fillMaxHeight()
.wrapContentWidth() // Allow the Box to wrap its content width .wrapContentWidth() // Allow the Box to wrap its content width
.padding(start = 16.dp), // Add some padding to separate from the Column .padding(start = 16.dp), // Add some padding to separate from the Column

View File

@ -5,8 +5,12 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.filled.Info
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuBox
@ -14,6 +18,7 @@ import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -39,8 +44,35 @@ fun AddCategory(
) { ) {
var name by remember { mutableStateOf("") } var name by remember { mutableStateOf("") }
var selectedIconIndex by remember { mutableStateOf(0) } // Store index of selected icon var selectedIconIndex by remember { mutableStateOf(-1) } // Store index of selected icon
var expanded by remember { mutableStateOf(false) } // Controls dropdown visibility var expanded by remember { mutableStateOf(false) } // Controls dropdown visibility
var parsingError by remember { mutableStateOf(false) }
if (parsingError) {
AlertDialog(
icon = {
Icon(Icons.Default.Info, contentDescription = "Info Icon")
},
title = {
Text(text = "Parsing error")
},
text = {
Text(text = "One or more fields contain invalid input. Please review your entries and try again.")
},
onDismissRequest = {
parsingError = false
},
confirmButton = {
TextButton(
onClick = {
parsingError = false
}
) {
Text("Okay")
}
}
)
}
Column( Column(
modifier = modifier modifier = modifier
@ -64,15 +96,23 @@ fun AddCategory(
onExpandedChange = { expanded = it } onExpandedChange = { expanded = it }
) { ) {
OutlinedTextField( OutlinedTextField(
value = iconNames[selectedIconIndex], // Show selected icon name value =
if (selectedIconIndex == -1){
""
}else{
iconNames[selectedIconIndex]
}
,
onValueChange = {}, onValueChange = {},
label = { Text("Icon") }, label = { Text("Icon") },
readOnly = true, readOnly = true,
leadingIcon = { leadingIcon = {
if (selectedIconIndex != -1){
Icon( Icon(
imageVector = icons[selectedIconIndex], // Replace with your desired icon imageVector = icons[selectedIconIndex], // Replace with your desired icon
contentDescription = "Leading Icon" contentDescription = "Leading Icon"
) )
}
}, },
trailingIcon = { trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
@ -113,7 +153,7 @@ fun AddCategory(
modifier = Modifier, modifier = Modifier,
"Add Category", "Add Category",
onClick = { onClick = {
if (name.isNotBlank()) { if (name.isNotBlank() && selectedIconIndex != -1) {
val newCategory = CategoryEntity( val newCategory = CategoryEntity(
_id = 0, _id = 0,
categoryName = name, categoryName = name,
@ -122,6 +162,8 @@ fun AddCategory(
categoryViewModel.addCategory(newCategory) categoryViewModel.addCategory(newCategory)
onButtonClick() onButtonClick()
}else{
parsingError = true
} }
} }
) )

View File

@ -21,6 +21,7 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -36,8 +37,6 @@ import at.xaxa.ledger.ui.ButtonSuccess
import at.xaxa.ledger.ui.category.iconNames import at.xaxa.ledger.ui.category.iconNames
import at.xaxa.ledger.ui.category.icons import at.xaxa.ledger.ui.category.icons
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun EditCategory( fun EditCategory(
@ -47,8 +46,8 @@ fun EditCategory(
) { ) {
val category = editCategoryViewModel.categoryUi.category val category = editCategoryViewModel.categoryUi.category
var showError by remember { mutableStateOf(editCategoryViewModel.deletePossible) } var showError by remember { mutableStateOf(editCategoryViewModel.deletePossible) }
var redirect by remember { mutableStateOf(editCategoryViewModel.redirect) } val redirect by editCategoryViewModel.redirect.collectAsState()
var deleteStarted by remember { mutableStateOf(false) } var deleteStarted by remember { mutableStateOf(editCategoryViewModel.deleteStarted) }
var expanded by remember { mutableStateOf(false) } var expanded by remember { mutableStateOf(false) }
var selectedIconIndex by remember { mutableIntStateOf(category.icon) } var selectedIconIndex by remember { mutableIntStateOf(category.icon) }
@ -60,8 +59,11 @@ fun EditCategory(
if (deleteStarted) { if (deleteStarted) {
editCategoryViewModel.findEntryByCategoryId() editCategoryViewModel.findEntryByCategoryId()
showError = editCategoryViewModel.deletePossible showError = editCategoryViewModel.deletePossible
} }
}
LaunchedEffect(redirect) {
Log.w("lolo", "EditCategory: "+ redirect.toString(), )
if (redirect) { if (redirect) {
onButtonClick() onButtonClick()
} }
@ -167,7 +169,7 @@ fun EditCategory(
ButtonDanger( ButtonDanger(
modifier = Modifier modifier = Modifier
.padding(bottom = 8.dp), .padding(bottom = 8.dp),
"Delete Category", "Delete",
onClick = { onClick = {
deleteStarted = true deleteStarted = true
editCategoryViewModel.findEntryByCategoryId() editCategoryViewModel.findEntryByCategoryId()
@ -175,7 +177,7 @@ fun EditCategory(
) )
ButtonSuccess( ButtonSuccess(
modifier = Modifier, modifier = Modifier,
"Save Category", "Done",
onClick = { onClick = {
editCategoryViewModel.saveCategory() editCategoryViewModel.saveCategory()
onButtonClick() onButtonClick()

View File

@ -10,6 +10,8 @@ import androidx.lifecycle.viewModelScope
import at.xaxa.ledger.data.EntryRepository import at.xaxa.ledger.data.EntryRepository
import at.xaxa.ledger.data.db.Category.CategoryEntity import at.xaxa.ledger.data.db.Category.CategoryEntity
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -18,17 +20,15 @@ data class CategoryUIState(val category: CategoryEntity = CategoryEntity(0,"",0)
class EditCategoryViewModel(private val savedStateHandle: SavedStateHandle, private val entryRepository: EntryRepository) : ViewModel() { class EditCategoryViewModel(private val savedStateHandle: SavedStateHandle, private val entryRepository: EntryRepository) : ViewModel() {
private val categoryId: Int = checkNotNull(savedStateHandle["categoryId"]) private val categoryId: Int = checkNotNull(savedStateHandle["categoryId"])
var categoryUi by mutableStateOf(CategoryUIState()) var categoryUi by mutableStateOf(CategoryUIState())
private set private set
var deletePossible by mutableStateOf(false) var deletePossible by mutableStateOf(false)
var redirect by mutableStateOf(false) var deleteStarted by mutableStateOf(false)
private var _redirect = MutableStateFlow(false)
val redirect: StateFlow<Boolean> get() = _redirect
init { init {
viewModelScope.launch { viewModelScope.launch {
@ -37,7 +37,7 @@ class EditCategoryViewModel(private val savedStateHandle: SavedStateHandle, priv
} }
categoryUi = CategoryUIState(category) categoryUi = CategoryUIState(category)
redirect = false; _redirect.value = false;
} }
} }
@ -45,7 +45,7 @@ class EditCategoryViewModel(private val savedStateHandle: SavedStateHandle, priv
categoryUi = categoryUi.copy(category = category) categoryUi = categoryUi.copy(category = category)
} }
fun deleteCategory() { fun deleteCategory(){
viewModelScope.launch { viewModelScope.launch {
entryRepository.deleteCategory(categoryUi.category) entryRepository.deleteCategory(categoryUi.category)
} }
@ -66,10 +66,10 @@ class EditCategoryViewModel(private val savedStateHandle: SavedStateHandle, priv
if(fetchedEntries>0){ if(fetchedEntries>0){
deletePossible = true deletePossible = true
}else{ }else{
Log.w("lolo", "findEntryByCategoryId: "+fetchedEntries, )
deleteCategory() deleteCategory()
redirect= true; _redirect.value= true;
} }
} }
//print("ASASDADS$categoryId")
} }
} }

View File

@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Info import androidx.compose.material.icons.filled.Info
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
@ -31,6 +32,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import at.xaxa.ledger.data.Entry import at.xaxa.ledger.data.Entry
@ -117,7 +119,8 @@ fun Add(
}, },
label = { Text("Amount") }, label = { Text("Amount") },
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth(),
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
) )
ExposedDropdownMenuBox( ExposedDropdownMenuBox(

View File

@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuBox
@ -27,6 +28,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import at.xaxa.ledger.data.Entry import at.xaxa.ledger.data.Entry
@ -94,7 +96,8 @@ fun Edit(
} }
}, },
label = { Text("Amount") }, label = { Text("Amount") },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth(),
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
) )
ExposedDropdownMenuBox( ExposedDropdownMenuBox(

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 KiB

255
README.md
View File

@ -11,7 +11,9 @@ Ledger is a personal finance management app that enables users to log and track
## App Concept ## App Concept
### Use Case ### Use Case
Primary Use Case: Primary Use Case:
- Tracking and Managing Personal Finances - Tracking and Managing Personal Finances
- Viewing Financial Summaries - Viewing Financial Summaries
- Logging Transactions - Logging Transactions
@ -20,19 +22,262 @@ Primary Use Case:
- Monitoring Budgets - Monitoring Budgets
### Target Users ### Target Users
People who want a simple and effective way to manage their personal finances. People who want a simple and effective way to manage their personal finances.
People who want to stay organized by tracking their income and expenses, categorizing transactions, and gaining insights into their financial habits. People who want to stay organized by tracking their income and expenses, categorizing transactions, and gaining insights into their financial habits.
Like: Like:
- Freelancers - Freelancers
- Students - Students
- Budget-Conscious Individuals - Budget-Conscious Individuals
- Small Business Owners - Small Business Owners
### Mockups ### Mockups
![Mockup of Start Screen](MockupStart.png) ![Mockup](ReportImages/mockup.png)
![Mockup of Add Screen](MockupAdd.png) ![Userflow](ReportImages/UserFlow.png)
![Mockup of Edit Screen](MockupEdit.png)
![Userflow](UserFlow.png) ### Database
![Database Design](ReportImages/database.png)
The database structure consists of two tables: entry and category. The entry table stores individual records with fields like `entryid` (`primary key`), ` name`, ` amount`, ` date`, and `categoryID` (`foreign key`), while the category table stores categories with fields like `_id` (`primary key`), ` categoryName`, and ` icon`. The `categoryID` in the entry table is designed to link entries to specific categories, enabling categorization and organization of data. This structure ensures efficient data management and retrieval based on categorized relationships.
## Usability Testing Plan
### Experimental Questions & Variables
1. Question: Did you find the app easy to use?
- IV: App navigation design (e.g., layout, menu structure, or usability features).
- DV: User-reported issues or difficulties while navigating.
- Methods: SEQ Question
2. Question: Does the user find Categories useful?
- IV: Presence or design of the Categories feature.
- DV: User perception of usefulness (e.g., measured via survey or rating).
- Method: Likert scale How useful was it?
3. Question: Does the user find Categories?
- IV: Visibility or accessibility of the Categories feature.
- DV: User success in locating or using Categories.
- Method: Task Completion Testing
4. Question: Does the icon selection affect user satisfaction?
- IV: Icon design or selection (e.g., different icon styles or clarity).
- DV: User satisfaction (e.g., measured via survey or rating).
- Method: Open Question
5. Question: How long does it take for a user to add a Category?
- IV: Process or interface for adding a Category (e.g., number of steps, clarity of instructions).
- DV: Time taken to complete the task.
- Method: Time till completion
6. Question: How long does it take for a user to add a Transaction?
- IV: Process or interface for adding a Transaction (e.g., simplicity, number of fields).
- DV: Time taken to complete the task.
- Method: Time till completion
### Heuristic Evaluation
Based off of Nielsens 10 Heuristics. Done by both team members based on the Mockup. Results were implemented in the android application.
1. Visibility of System Status
- Issue: Users may not be immediately aware of changes to their balance after adding or editing a transaction, as there is no clear feedback.
- Recommendations: Implement a visual or auditory feedback system (e.g., a confirmation message or animation or redirect to the Balance overview) to inform users when their balance updates.
2. Match Between System and the Real World
- Issues: None
- Recommendations: None
3. User Control and Freedom
- Issues: Users might want to create their own categories rather than use the predefined ones.
- Recommendations: Introduce a feature to add and delete your own categories.
4. Consistency and Standards
- Issues: The "Your Balance" and the name of the transaction do not share the same font
- Recommendations: Standardize all texts to use the same font, and color scheme for a cohesive user experience.
5. Error Prevention
- Issues: Users might enter invalid data, such as symbols as the amount, without receiving a warning, leading to potential errors.
- Recommendations: Add validation checks and warnings to prevent users from entering incorrect data, ensuring errors are caught before submission.
6. Recognition Rather Than Recall
- Issues: Users may need to remember details about past transactions, such as categories or dates, which could increase cognitive load.
- Recommendations: Display a detailed transaction history with categories and dates visible at all times, reducing the need for users to recall information.
7. Flexibility and Efficiency of Use
- Issues: Users may find it time-consuming to navigate through multiple screens or steps to perform common tasks, such as adding or editing transactions.
- Recommendations: Ensure that all frequently used actions, like adding or deleting transactions, can be completed in as few steps as possible, with clear and accessible buttons for quick access.
8. Aesthetic and Minimalist Design
- Issues: The interface might include too many visual elements or colors, which could distract users from focusing on their primary tasks, such as managing transactions or checking their balance.
- Recommendations: Ensure that the design remains clean and focused by using a limited color palette and removing any unnecessary decorative elements, keeping the interface visually simple and task-oriented.
9. Help Users Recognize, Diagnose, and Recover from Errors
- Issues: Error messages may not clearly explain what went wrong or how to fix it, leaving users confused.
- Recommendations: Provide detailed error messages in plain language, including actionable steps to resolve the issue.
10. Help and Documentation
- Issues: Help and Documentation may be important and nice to have but out of scope for this version of the design.
- Recommendations: For now, focus on maintaining the clarity and simplicity of the current help documentation. If these advanced features are considered in future updates, ensure that corresponding help resources are developed to support them.
## Results
### Participant Demographics
- **Number of Test Subjects:** 5
- **Demographics:** University Students aged 19-23
---
### Quantitative Evaluation
#### Task Completion Time
##### Adding a Category:
| Participant | Time (seconds) |
| ----------- | -------------- |
| 1 | 18 |
| 2 | 69 |
| 3 | 40 |
| 4 | 18 |
| 5 | 15 |
**Average Time:** 32 seconds
##### Adding a Transaction:
| Participant | Time (seconds) |
| ----------- | -------------- |
| 1 | 18 |
| 2 | 11 |
| 3 | 60 |
| 4 | 43 |
| 5 | 30 |
**Average Time:** 32.4 seconds
![Time to add Category vs. Time to add Transaction](ReportImages/timeToAddCategory_vs_timeToAddTransaction.jpg)
**Time to Add Category vs. Time to Add Transaction:**
- The box plot shows variability in task completion times, with some participants taking significantly longer than others.
- For **adding a category**, Participant 2 took 69 seconds, which is an outlier compared to the others (1540 seconds).
- For **adding a transaction**, Participant 3 took 60 seconds, while others completed the task in 1143 seconds.
- This variability suggests that some users may have struggled with the interface or navigation, particularly for adding categories.
---
#### User Satisfaction Scores
##### Ease of Use (Scale: 1-7):
| Participant | Score |
| ----------- | ----- |
| 1 | 7 |
| 2 | 5 |
| 3 | 7 |
| 4 | 7 |
| 5 | 6 |
**Average Score:** 6.4
![Ease of use Graph](ReportImages/easeOfUse.jpg)
**Ease of use:**
- The bar chart shows that most participants rated the app highly for ease of use, with four out of five giving scores of 6 or higher.
- Participant 2 gave a lower score of 5, which aligns with their feedback about difficulty finding categories and longer task completion times.
- The chart reinforces the overall positive perception of the app's usability but highlights the need to address specific pain points for users like Participant 2.
---
#### Finding Categories and their Usefulness
##### Usefulness of Categories (Scale: 1-5) & Found Categories (Scale: Yes/No):
| Participant | Score | Found |
| ----------- | ----- | ----- |
| 1 | 3 | Yes |
| 2 | 4 | No |
| 3 | 4 | Yes |
| 4 | 5 | Yes |
| 5 | 5 | Yes |
**Average Score:** 4.2
#### Task Success Rate
![Success rate of finding categories.jpg](ReportImages/successRateOfFindingCategories.jpg)
**Finding Categories:**
- The bar chart visually confirms that the majority of participants (80%) successfully found the categories.
- However, 1 participant who struggled is representing areas for improvement.
---
#### Usefulness of Categories vs. Found Categories
![Usefulness of categories vs. Found categories](ReportImages/usefulnessOfCategories_vs_foundCategories.jpg)
**Usefulness of Categories vs. Found Categories:**
- The stacked bar chart shows a strong correlation between finding categories and perceiving them as useful.
- Four out of five participants found the categories and rated their usefulness highly (scores of 4 or 5).
- Participant 2, who did not find the categories initially, still rated their usefulness as 4, suggesting that they recognized the potential value once they located them.
- The chart highlights the importance of making categories easy to find, as discoverability directly impacts perceived usefulness.
---
### Qualitative Feedback
**Positive Feedback:**
- "The app is easy to use."
- "Categories are useful for organizing transactions."
**Negative Feedback:**
- "I could not find the categories initially."
- "The icon selection is missing a Pets category."
**Suggestions for Improvement:**
- "Add a Pet Icon.”
- "Improve the visibility of categories in the app."
---
### Updates Made to the App
Based on the feedback and insights from the charts, the following updates were made:
1. **Improved Visibility of System Status:**
- Added headings for _Category_ and _Transaction_ screens to make navigation more intuitive.
2. **Improved Category Visibility:**
- Redesigned the categories section to make it more prominent and easier to find, addressing the issue raised by Participant 2.
---
### Reflection
#### Xaver Drabik
I really enjoyed working with Florian on this project. Combining our strengths helped us create a functional and user-friendly app. Usability testing was particularly rewarding, offering insights into UI/UX design I wouldnt have considered otherwise. Watching real users interact with the app revealed areas for improvement I might have missed. This experience deepened my appreciation for user-centered design and the importance of testing in creating a polished product.
#### Florian Kraushofer
I had a great time working on this project. My main focus was on the backend, and I really enjoyed tackling the coding challenges and making sure everything ran smoothly. It was cool to see how my work connected with the frontend to create a functional app. The usability testing phase was eye-opening, it showed me how small design tweaks can make a big difference for users. Overall, it was a fun and rewarding experience, and I learned a lot about both development and the importance of keeping the end user in mind.

View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

BIN
ReportImages/easeOfUse.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
ReportImages/mockup.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB