Files
Clario/Clario/MobileViews/AnalyticsViewMobile.axaml
Nouredeen06 61ff949c19
Some checks failed
Build Linux / build (push) Failing after 24s
Add analytics page, auth error handling, and period navigation fix
Features
Analytics Page: Full-featured analytics dashboard with KPI cards, cash flow trend chart, net worth progression, spending patterns by day-of-week, top spending categories, and income sources breakdown. Includes PDF export via QuestPDF for selected periods. Implemented on both desktop and mobile (simplified).
Auth Error Handling: Map Supabase GotrueException errors to AuthError enum with user-friendly messages for login and signup. Display errors in sign-in and sign-up panels.
Dynamic Transaction/Account Counts: Replace hardcoded "46 transactions" and "4 accounts" text with FilteredTransactionCount and ActiveAccountCount properties bound to actual data.

Fixes
Budget Period Navigation: Fix year-aware date comparison in CanGoToPreviousPeriod and CanGoToNextPeriod. Previously only compared months, preventing navigation before January of current year.

Changes
AnalyticsViewModel: Period selector, KPI calculations, chart data builders (cash flow, net worth, day-of-week, top categories, income sources), PDF export
PdfExportService: QuestPDF report generation with print-optimized styling
AuthViewModel: Error display with GotrueException mapping
BudgetViewModel: Year-aware period navigation
TransactionsViewModel: FilteredTransactionCount property
AccountsViewModel: ActiveAccountCount property
MainViewModel: Analytics navigation and AnalyticsViewModel integration
Views: Analytics button wired, error messages displayed, count bindings updated
2026-04-05 23:08:34 +03:00

162 lines
9.6 KiB
XML

<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:Clario.ViewModels"
xmlns:model="clr-namespace:Clario.Models"
xmlns:lvc="using:LiveChartsCore.SkiaSharpView.Avalonia"
mc:Ignorable="d"
Classes="mobile"
x:DataType="vm:AnalyticsViewModel"
x:Class="Clario.MobileViews.AnalyticsViewMobile">
<Design.DataContext>
<vm:AnalyticsViewModel />
</Design.DataContext>
<Grid RowDefinitions="Auto,*">
<!-- Top bar -->
<Grid Grid.Row="0" ColumnDefinitions="*,Auto" Margin="16,12,16,12">
<StackPanel Grid.Column="0">
<TextBlock Classes="muted" Text="Insights &amp; Trends" FontSize="12" />
<TextBlock Text="Analytics" FontSize="22" FontWeight="Bold"
Foreground="{DynamicResource TextPrimary}" Margin="0,2,0,0" />
</StackPanel>
<ComboBox Grid.Column="1" ItemsSource="{Binding PeriodOptions}"
SelectedItem="{Binding SelectedPeriod}"
Background="{DynamicResource BgSurface}"
Foreground="{DynamicResource TextSecondary}"
BorderBrush="{DynamicResource BorderSubtle}"
CornerRadius="{DynamicResource RadiusControl}"
Padding="8,5" FontSize="12"
VerticalAlignment="Center" />
</Grid>
<ScrollViewer Grid.Row="1"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<StackPanel Spacing="14" Margin="16,0,16,24">
<!-- KPI Cards (2x2 grid) -->
<Grid ColumnDefinitions="*,*" RowDefinitions="Auto,Auto">
<!-- Total Income -->
<Border Grid.Row="0" Grid.Column="0" Classes="card" Margin="0,0,7,7">
<StackPanel Spacing="8">
<TextBlock Classes="label" Text="INCOME" />
<TextBlock Text="{Binding TotalIncomeFormatted}" FontSize="16" FontWeight="Bold"
Foreground="{DynamicResource AccentGreen}" />
</StackPanel>
</Border>
<!-- Total Expenses -->
<Border Grid.Row="0" Grid.Column="1" Classes="card" Margin="7,0,0,7">
<StackPanel Spacing="8">
<TextBlock Classes="label" Text="EXPENSES" />
<TextBlock Text="{Binding TotalExpensesFormatted}" FontSize="16" FontWeight="Bold"
Foreground="{DynamicResource AccentRed}" />
</StackPanel>
</Border>
<!-- Net Savings -->
<Border Grid.Row="1" Grid.Column="0" Classes="card" Margin="0,0,7,0">
<StackPanel Spacing="8">
<TextBlock Classes="label" Text="NET SAVINGS" />
<TextBlock Text="{Binding NetSavingsFormatted}" FontSize="16" FontWeight="Bold"
Foreground="{Binding NetSavingsPositive, Converter={StaticResource BoolToColorConverter}, ConverterParameter='#2ECC8A|#FF5E5E'}" />
</StackPanel>
</Border>
<!-- Savings Rate -->
<Border Grid.Row="1" Grid.Column="1" Classes="card" Margin="7,0,0,0">
<StackPanel Spacing="8">
<TextBlock Classes="label" Text="SAVINGS RATE" />
<TextBlock Text="{Binding SavingsRateFormatted}" FontSize="16" FontWeight="Bold"
Foreground="{DynamicResource AccentPurple}" />
</StackPanel>
</Border>
</Grid>
<!-- Cash Flow Chart -->
<Border Classes="card">
<StackPanel Spacing="12">
<TextBlock Text="Cash Flow Trend" FontSize="14" FontWeight="SemiBold"
Foreground="{DynamicResource TextPrimary}" />
<lvc:CartesianChart Series="{Binding CashFlowSeries}"
XAxes="{Binding CashFlowXAxes}"
YAxes="{Binding CashFlowYAxes}"
Height="200"
Background="{DynamicResource BgSurface}"
LegendPosition="Bottom"
TooltipPosition="Top"
ZoomMode="None"
AnimationsSpeed="00:00:00.2" />
</StackPanel>
</Border>
<!-- Net Worth -->
<Border Classes="card">
<StackPanel Spacing="12">
<TextBlock Text="Net Worth" FontSize="14" FontWeight="SemiBold"
Foreground="{DynamicResource TextPrimary}" />
<lvc:CartesianChart Series="{Binding NetWorthSeries}"
XAxes="{Binding NetWorthXAxes}"
YAxes="{Binding NetWorthYAxes}"
Height="180"
Background="{DynamicResource BgSurface}"
LegendPosition="Hidden"
TooltipPosition="Top"
ZoomMode="None"
AnimationsSpeed="00:00:00.2" />
</StackPanel>
</Border>
<!-- Top Categories -->
<Border Classes="card">
<StackPanel Spacing="12">
<TextBlock Text="Top Spending Categories" FontSize="14" FontWeight="SemiBold"
Foreground="{DynamicResource TextPrimary}" />
<TextBlock Text="No expense data for this period."
Classes="muted" IsVisible="{Binding !HasTopCategories}" FontSize="12" />
<ItemsControl ItemsSource="{Binding TopCategories}"
IsVisible="{Binding HasTopCategories}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Spacing="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="model:CategorySpendRow">
<Border Background="{DynamicResource BgHover}" CornerRadius="10"
Padding="12,10" Margin="0,0,0,1">
<Grid ColumnDefinitions="Auto,*,Auto">
<Border Grid.Column="0" CornerRadius="7" Width="30" Height="30" Margin="0,0,10,0">
<Border.Background>
<SolidColorBrush Color="{Binding Color, Converter={StaticResource HexToColorConverter}, ConverterParameter=color}"
Opacity="0.15" />
</Border.Background>
<Svg Path="{Binding Icon, Converter={StaticResource SvgPathFromName}}"
Width="14" Height="14"
Css="{Binding Color, Converter={StaticResource HexToColorConverter}, ConverterParameter=css}" />
</Border>
<StackPanel Grid.Column="1" VerticalAlignment="Center" Spacing="4">
<TextBlock Text="{Binding Name}" FontSize="12" FontWeight="SemiBold"
Foreground="{DynamicResource TextPrimary}" />
<ProgressBar Value="{Binding Percentage}" Minimum="0" Maximum="100"
Height="3" Classes="blue" CornerRadius="2" />
</StackPanel>
<TextBlock Grid.Column="2" Text="{Binding AmountFormatted}"
FontSize="12" FontWeight="SemiBold"
Foreground="{DynamicResource TextPrimary}"
VerticalAlignment="Center" Margin="8,0,0,0" />
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</Grid>
</UserControl>