Some checks failed
Build Linux / build (push) Failing after 24s
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
162 lines
9.6 KiB
XML
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 & 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>
|