Update German localization for button labels and add photo requirement validation in Profile views

This commit is contained in:
Max Mannstein 2025-09-22 21:52:26 +02:00
parent adf335d2be
commit fe828683bd
7 changed files with 191 additions and 4 deletions

145
.github/copilot-instructions.md vendored Normal file
View File

@ -0,0 +1,145 @@
# gehGassi App - AI Coding Agent Instructions
## Project Overview
**gehGassi** is a .NET 9 MAUI cross-platform mobile app for a dog walking marketplace connecting dog owners with dog walkers. The app features dual user types (owners and walkers), real-time messaging, walk booking, payments, ratings, and a complex business workflow.
## Architecture & Key Patterns
### Project Structure (Clean Architecture)
- **gehGassiApp** - Main MAUI app with Views, ViewModels, platform-specific code
- **gehGassiApp.Core** - Business logic, services, interfaces
- **gehGassiApp.Domain** - Entity models, value objects
- **gehGassiApp.Data** - Entity Framework Core, SQLite database, repository pattern
- **gehGassi.LocalNotifications** - Custom notification library
- **gehGassi.Dto** - Shared DTOs with backend (external project reference)
### MVVM + DI Pattern
- **BaseViewModel/MenuViewModel** - Inherit from these base classes for consistent patterns
- **CommunityToolkit.Mvvm** - Uses `[ObservableProperty]` and `[RelayCommand]` attributes
- **Dependency Injection** - All services registered in `MauiProgram.cs`, constructor injection throughout
- **Page Lifecycle** - Override `InitializeAsync()` and `DisappearingAsync()` in ViewModels
### Service Layer Architecture
All business services follow `IService<T>` interface with standard CRUD operations:
```csharp
// Example service injection in ViewModels
public MyViewModel(IWalkService walkService, IDialogService dialogService) { }
```
Key service types:
- **Data Services** - Entity management (e.g., `IWalkService`, `IDogService`)
- **Integration Services** - API communication (`ICommunicationService`)
- **Platform Services** - Native features (`ILocationService`, `IDeviceInstallationService`)
- **UI Services** - User interactions (`IDialogService`, `IPopupService`)
### Configuration Management
Environment-specific settings in `MauiProgram.cs`:
- **DEBUG** - Local development server (`192.168.0.172:8548`)
- **STAGING/DEBUGSTAGING** - Staging environment
- **RELEASE** - Production (`backend.gehgassi.com`)
Application ID changes per environment:
- Debug/Staging: `com.gehgassi.local`
- Release: `com.gehgassi.gehgassi`
## Critical Development Patterns
### XAML Resources System
**ALWAYS verify resource existence before use** - the app has strict resource validation:
- **Converters**: Defined in `Resources/Converters/Converters.xaml` (30+ available)
- **Styles**: Defined in `Resources/Styles/Styles.xaml` (H5_Red, BodyCaption variants, etc.)
- **Reference**: See `gehGassiApp_Converters_And_Styles.md` for complete list
Example of correct resource usage:
```xml
<!-- Correct - uses existing resources -->
<Label Text="Error" Style="{StaticResource H5_Red}"
IsVisible="{Binding HasError, Converter={StaticResource InvertedBoolConverter}}" />
```
### Data Binding Patterns
- **Validation**: Use multi-binding for button states:
```xml
<Button Text="Save">
<Button.IsEnabled>
<MultiBinding Converter="{StaticResource AllTrueMultiConverter}">
<Binding Path="IsValid" />
<Binding Path="HasPhoto" />
</MultiBinding>
</Button.IsEnabled>
</Button>
```
### Platform-Specific Code
- **Android**: `Platforms/Android/` - Firebase services, permissions
- **iOS**: `Platforms/iOS/` - Push notifications, App Store compliance
- **Handlers**: Custom control behaviors in `MauiProgram.SetHandler()`
### Local Database (EF Core + SQLite)
- **Context**: `LocalDbContext` with automatic migrations
- **Repository Pattern**: `IUnitOfWork` coordinates multiple services
- **Sync Pattern**: Many services implement `ISyncPushPullService<T>` for offline capability
## Build & Development
### Build Configurations
- **Debug** - Local development, APK format
- **DebugStaging** - Staging with debug symbols
- **Release** - Production, AAB format with AOT compilation
- **Staging** - Pre-production testing
### Build Commands
```bash
# Build for specific platform/config
dotnet build -f net9.0-android -c Debug
dotnet build -f net9.0-ios -c Release
# Platform-specific from main project directory
dotnet build gehGassiApp/gehGassiApp.csproj -f net9.0-android
```
### Critical Dependencies
- **.NET 9** with MAUI 9.0.90
- **Entity Framework Core 9.0.8**
- **CommunityToolkit.Mvvm 8.4.0** - MVVM framework
- **Microsoft.AspNetCore.SignalR.Client** - Real-time communication
- **Platform packages**: Xamarin.Firebase.Messaging (Android), various AndroidX libraries
## Business Domain Knowledge
### User Types & Workflows
- **Dog Owners** - Create walk requests, book walkers, manage dogs, rate walkers
- **Dog Walkers** - Browse requests, offer services, execute walks, receive payments
- **Dual Users** - Can switch between owner/walker modes
### Key Entities
- **Walk** - Core business object with pickup/return addresses, timing, pricing
- **PublicWalkRequest/Response** - Marketplace discovery system
- **Rating** - Bidirectional rating system for quality assurance
- **Conversation/Message** - In-app communication with SignalR real-time updates
### Complex Features
- **Payment Flow** - Integration with external payment providers, wallet system
- **Location Services** - GPS tracking during walks, geofenced operations
- **Push Notifications** - Azure Notification Hub for engagement
- **Offline Sync** - Local-first architecture with background synchronization
## Common Issues & Solutions
### Resource Not Found Errors
Always check `Resources/Converters/Converters.xaml` and `Resources/Styles/Styles.xaml` before adding new resources.
### Navigation Patterns
Use Shell-based navigation with query parameters:
```csharp
await Shell.Current.GoToAsync($"WalkView?walkId={walkId}");
```
### Memory Management
Custom image handler prevents crashes on Android - avoid modifying existing image loading patterns without testing.
### Platform Testing
Test on both iOS and Android as business workflows have platform-specific behaviors (notifications, payments, camera).
---
**Note**: This app serves a real marketplace with financial transactions. Always test payment flows, rating systems, and user safety features thoroughly.

View File

@ -268,7 +268,7 @@
<value>Meine Hunde</value> <value>Meine Hunde</value>
</data> </data>
<data name="Button_Done" xml:space="preserve"> <data name="Button_Done" xml:space="preserve">
<value>Fertig</value> <value>Speichern</value>
</data> </data>
<data name="Button_EditOffer" xml:space="preserve"> <data name="Button_EditOffer" xml:space="preserve">
<value>Angebot bearbeiten</value> <value>Angebot bearbeiten</value>

View File

@ -268,7 +268,7 @@
<value>My dogs</value> <value>My dogs</value>
</data> </data>
<data name="Button_Done" xml:space="preserve"> <data name="Button_Done" xml:space="preserve">
<value>Done</value> <value>Save</value>
</data> </data>
<data name="Button_EditOffer" xml:space="preserve"> <data name="Button_EditOffer" xml:space="preserve">
<value>Edit offer</value> <value>Edit offer</value>

View File

@ -75,6 +75,11 @@ namespace gehGassiApp.ViewModels
[ObservableProperty] [ObservableProperty]
private bool _isNewPhoto; private bool _isNewPhoto;
/// <summary>
/// Hat der Benutzer ein Profilbild?
/// </summary>
public bool HasPhoto => !string.IsNullOrWhiteSpace(AppUser?.Photo);
/// <summary> /// <summary>
/// Liste der Länder /// Liste der Länder
/// </summary> /// </summary>
@ -266,6 +271,7 @@ namespace gehGassiApp.ViewModels
} }
CheckForChanges(); CheckForChanges();
OnPropertyChanged(nameof(HasPhoto));
} }
partial void OnIsNewPhotoChanged(bool value) partial void OnIsNewPhotoChanged(bool value)
@ -648,6 +654,12 @@ namespace gehGassiApp.ViewModels
private void OnAppUserPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) private void OnAppUserPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{ {
CheckForChanges(); CheckForChanges();
// HasPhoto aktualisieren wenn sich die Photo Property ändert
if (e.PropertyName == nameof(AppUser.Photo))
{
OnPropertyChanged(nameof(HasPhoto));
}
} }
#endregion #endregion

View File

@ -88,6 +88,11 @@ namespace gehGassiApp.ViewModels
[ObservableProperty] [ObservableProperty]
private bool _isNewPhoto; private bool _isNewPhoto;
/// <summary>
/// Hat der Benutzer ein Profilbild?
/// </summary>
public bool HasPhoto => !string.IsNullOrWhiteSpace(AppUser?.Photo);
/// <summary> /// <summary>
/// Liste der Länder /// Liste der Länder
/// </summary> /// </summary>
@ -324,6 +329,7 @@ namespace gehGassiApp.ViewModels
} }
CheckForChanges(); CheckForChanges();
OnPropertyChanged(nameof(HasPhoto));
} }
partial void OnWalkerProfileChanged(DogWalkerProfile value) partial void OnWalkerProfileChanged(DogWalkerProfile value)
@ -797,6 +803,12 @@ namespace gehGassiApp.ViewModels
private void OnAppUserPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) private void OnAppUserPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{ {
CheckForChanges(); CheckForChanges();
// HasPhoto aktualisieren wenn sich die Photo Property ändert
if (e.PropertyName == nameof(AppUser.Photo))
{
OnPropertyChanged(nameof(HasPhoto));
}
} }
/// <summary> /// <summary>

View File

@ -174,6 +174,14 @@
</Grid> </Grid>
<Label
Margin="{StaticResource MarginSmall}"
HorizontalOptions="Center"
IsVisible="{Binding HasPhoto, Converter={StaticResource InvertedBoolConverter}}"
Style="{StaticResource H5_Red}"
Text="Profilbild ist erforderlich"
VerticalOptions="Start" />
<VerticalStackLayout <VerticalStackLayout
HorizontalOptions="Fill" HorizontalOptions="Fill"
Spacing="0" Spacing="0"
@ -509,7 +517,7 @@
<Button <Button
x:Name="btnSave" x:Name="btnSave"
Margin="0,0,0,20" Margin="0,0,0,20"
AbsoluteLayout.LayoutBounds="0.5,1,300,60" AbsoluteLayout.LayoutBounds="0.5,1,350,70"
AbsoluteLayout.LayoutFlags="PositionProportional" AbsoluteLayout.LayoutFlags="PositionProportional"
Command="{Binding SaveCommand}" Command="{Binding SaveCommand}"
HorizontalOptions="Center" HorizontalOptions="Center"
@ -528,6 +536,7 @@
<Binding Path="IsValid" Source="{x:Reference mobileValidation}" /> <Binding Path="IsValid" Source="{x:Reference mobileValidation}" />
<Binding Path="IsValid" Source="{x:Reference phoneValidation}" /> <Binding Path="IsValid" Source="{x:Reference phoneValidation}" />
<Binding Path="AddressSelectionValid" /> <Binding Path="AddressSelectionValid" />
<Binding Path="HasPhoto" />
<Binding Path="IsNotBusy" /> <Binding Path="IsNotBusy" />
</MultiBinding> </MultiBinding>
</Button.IsEnabled> </Button.IsEnabled>

View File

@ -230,6 +230,14 @@
</Grid> </Grid>
<Label
Margin="{StaticResource MarginSmall}"
HorizontalOptions="Center"
IsVisible="{Binding HasPhoto, Converter={StaticResource InvertedBoolConverter}}"
Style="{StaticResource H5_Red}"
Text="Profilbild ist erforderlich"
VerticalOptions="Start" />
<VerticalStackLayout <VerticalStackLayout
Margin="{StaticResource MarginSection}" Margin="{StaticResource MarginSection}"
HorizontalOptions="Fill" HorizontalOptions="Fill"
@ -1016,7 +1024,7 @@
<Button <Button
x:Name="btnSave" x:Name="btnSave"
Margin="0,0,0,20" Margin="0,0,0,20"
AbsoluteLayout.LayoutBounds="0.5,1,300,60" AbsoluteLayout.LayoutBounds="0.5,1,350,70"
AbsoluteLayout.LayoutFlags="PositionProportional" AbsoluteLayout.LayoutFlags="PositionProportional"
Command="{Binding SaveCommand}" Command="{Binding SaveCommand}"
HorizontalOptions="Center" HorizontalOptions="Center"
@ -1035,6 +1043,7 @@
<Binding Path="IsValid" Source="{x:Reference mobileValidation}" /> <Binding Path="IsValid" Source="{x:Reference mobileValidation}" />
<Binding Path="IsValid" Source="{x:Reference phoneValidation}" /> <Binding Path="IsValid" Source="{x:Reference phoneValidation}" />
<Binding Path="AddressSelectionValid" /> <Binding Path="AddressSelectionValid" />
<Binding Path="HasPhoto" />
<Binding Path="IsNotBusy" /> <Binding Path="IsNotBusy" />
</MultiBinding> </MultiBinding>
</Button.IsEnabled> </Button.IsEnabled>