diff --git a/Clario.Android/Clario.Android.csproj b/Clario.Android/Clario.Android.csproj index c4ed782..bd7e303 100644 --- a/Clario.Android/Clario.Android.csproj +++ b/Clario.Android/Clario.Android.csproj @@ -13,16 +13,13 @@ Clario.Android - - - Resources\drawable\Icon.png - - - + + + @@ -30,4 +27,9 @@ + + + + + diff --git a/Clario.Android/Icon.png b/Clario.Android/Icon.png deleted file mode 100644 index e69de29..0000000 diff --git a/Clario.Android/Resources/drawable-night-v31/avalonia_anim.xml b/Clario.Android/Resources/drawable-night-v31/avalonia_anim.xml deleted file mode 100644 index dde4b5a..0000000 --- a/Clario.Android/Resources/drawable-night-v31/avalonia_anim.xml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Clario.Android/Resources/drawable-v31/avalonia_anim.xml b/Clario.Android/Resources/drawable-v31/avalonia_anim.xml deleted file mode 100644 index 94f27d9..0000000 --- a/Clario.Android/Resources/drawable-v31/avalonia_anim.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Clario.Android/Resources/drawable/Icon.png b/Clario.Android/Resources/drawable/Icon.png new file mode 100644 index 0000000..609b617 Binary files /dev/null and b/Clario.Android/Resources/drawable/Icon.png differ diff --git a/Clario.Android/Resources/drawable/splash_screen.xml b/Clario.Android/Resources/drawable/splash_screen.xml index 2e920b4..3943420 100644 --- a/Clario.Android/Resources/drawable/splash_screen.xml +++ b/Clario.Android/Resources/drawable/splash_screen.xml @@ -5,7 +5,7 @@ - diff --git a/Clario.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml b/Clario.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..345888d --- /dev/null +++ b/Clario.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Clario.Android/Resources/mipmap-hdpi/ic_launcher.png b/Clario.Android/Resources/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..a471a68 Binary files /dev/null and b/Clario.Android/Resources/mipmap-hdpi/ic_launcher.png differ diff --git a/Clario.Android/Resources/mipmap-hdpi/ic_launcher_background.png b/Clario.Android/Resources/mipmap-hdpi/ic_launcher_background.png new file mode 100644 index 0000000..d814e91 Binary files /dev/null and b/Clario.Android/Resources/mipmap-hdpi/ic_launcher_background.png differ diff --git a/Clario.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png b/Clario.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..540359a Binary files /dev/null and b/Clario.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/Clario.Android/Resources/mipmap-hdpi/ic_launcher_monochrome.png b/Clario.Android/Resources/mipmap-hdpi/ic_launcher_monochrome.png new file mode 100644 index 0000000..540359a Binary files /dev/null and b/Clario.Android/Resources/mipmap-hdpi/ic_launcher_monochrome.png differ diff --git a/Clario.Android/Resources/mipmap-mdpi/ic_launcher.png b/Clario.Android/Resources/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..9fbfaff Binary files /dev/null and b/Clario.Android/Resources/mipmap-mdpi/ic_launcher.png differ diff --git a/Clario.Android/Resources/mipmap-mdpi/ic_launcher_background.png b/Clario.Android/Resources/mipmap-mdpi/ic_launcher_background.png new file mode 100644 index 0000000..29ad737 Binary files /dev/null and b/Clario.Android/Resources/mipmap-mdpi/ic_launcher_background.png differ diff --git a/Clario.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png b/Clario.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..ac8b67f Binary files /dev/null and b/Clario.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/Clario.Android/Resources/mipmap-mdpi/ic_launcher_monochrome.png b/Clario.Android/Resources/mipmap-mdpi/ic_launcher_monochrome.png new file mode 100644 index 0000000..ac8b67f Binary files /dev/null and b/Clario.Android/Resources/mipmap-mdpi/ic_launcher_monochrome.png differ diff --git a/Clario.Android/Resources/mipmap-xhdpi/ic_launcher.png b/Clario.Android/Resources/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..b73651f Binary files /dev/null and b/Clario.Android/Resources/mipmap-xhdpi/ic_launcher.png differ diff --git a/Clario.Android/Resources/mipmap-xhdpi/ic_launcher_background.png b/Clario.Android/Resources/mipmap-xhdpi/ic_launcher_background.png new file mode 100644 index 0000000..280bfe0 Binary files /dev/null and b/Clario.Android/Resources/mipmap-xhdpi/ic_launcher_background.png differ diff --git a/Clario.Android/Resources/mipmap-xhdpi/ic_launcher_foreground.png b/Clario.Android/Resources/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..94a8d21 Binary files /dev/null and b/Clario.Android/Resources/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/Clario.Android/Resources/mipmap-xhdpi/ic_launcher_monochrome.png b/Clario.Android/Resources/mipmap-xhdpi/ic_launcher_monochrome.png new file mode 100644 index 0000000..94a8d21 Binary files /dev/null and b/Clario.Android/Resources/mipmap-xhdpi/ic_launcher_monochrome.png differ diff --git a/Clario.Android/Resources/mipmap-xxhdpi/ic_launcher.png b/Clario.Android/Resources/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..f47d9d8 Binary files /dev/null and b/Clario.Android/Resources/mipmap-xxhdpi/ic_launcher.png differ diff --git a/Clario.Android/Resources/mipmap-xxhdpi/ic_launcher_background.png b/Clario.Android/Resources/mipmap-xxhdpi/ic_launcher_background.png new file mode 100644 index 0000000..b76da7e Binary files /dev/null and b/Clario.Android/Resources/mipmap-xxhdpi/ic_launcher_background.png differ diff --git a/Clario.Android/Resources/mipmap-xxhdpi/ic_launcher_foreground.png b/Clario.Android/Resources/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..241af26 Binary files /dev/null and b/Clario.Android/Resources/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/Clario.Android/Resources/mipmap-xxhdpi/ic_launcher_monochrome.png b/Clario.Android/Resources/mipmap-xxhdpi/ic_launcher_monochrome.png new file mode 100644 index 0000000..241af26 Binary files /dev/null and b/Clario.Android/Resources/mipmap-xxhdpi/ic_launcher_monochrome.png differ diff --git a/Clario.Android/Resources/mipmap-xxxhdpi/ic_launcher.png b/Clario.Android/Resources/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..bf9bbc1 Binary files /dev/null and b/Clario.Android/Resources/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/Clario.Android/Resources/mipmap-xxxhdpi/ic_launcher_background.png b/Clario.Android/Resources/mipmap-xxxhdpi/ic_launcher_background.png new file mode 100644 index 0000000..b90fa91 Binary files /dev/null and b/Clario.Android/Resources/mipmap-xxxhdpi/ic_launcher_background.png differ diff --git a/Clario.Android/Resources/mipmap-xxxhdpi/ic_launcher_foreground.png b/Clario.Android/Resources/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..1b432ff Binary files /dev/null and b/Clario.Android/Resources/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/Clario.Android/Resources/mipmap-xxxhdpi/ic_launcher_monochrome.png b/Clario.Android/Resources/mipmap-xxxhdpi/ic_launcher_monochrome.png new file mode 100644 index 0000000..1b432ff Binary files /dev/null and b/Clario.Android/Resources/mipmap-xxxhdpi/ic_launcher_monochrome.png differ diff --git a/Clario.Android/Resources/values-v31/styles.xml b/Clario.Android/Resources/values-v31/styles.xml index d5ecec4..9f70053 100644 --- a/Clario.Android/Resources/values-v31/styles.xml +++ b/Clario.Android/Resources/values-v31/styles.xml @@ -9,7 +9,6 @@ @null true @color/splash_background - @drawable/avalonia_anim 1000 @style/MyTheme.Main diff --git a/Clario.Android/Resources/values/colors.xml b/Clario.Android/Resources/values/colors.xml index 59279d5..6e1ec38 100644 --- a/Clario.Android/Resources/values/colors.xml +++ b/Clario.Android/Resources/values/colors.xml @@ -1,4 +1,4 @@  - #FFFFFF + #0B0D12 diff --git a/Clario.Android/play_store_512.png b/Clario.Android/play_store_512.png new file mode 100644 index 0000000..d616c0e Binary files /dev/null and b/Clario.Android/play_store_512.png differ diff --git a/Clario.Browser/Clario.Browser.csproj b/Clario.Browser/Clario.Browser.csproj index 29125f6..9a83c58 100644 --- a/Clario.Browser/Clario.Browser.csproj +++ b/Clario.Browser/Clario.Browser.csproj @@ -9,7 +9,10 @@ + + + diff --git a/Clario.Desktop/Clario.Desktop.csproj b/Clario.Desktop/Clario.Desktop.csproj index ae21efd..484e86d 100644 --- a/Clario.Desktop/Clario.Desktop.csproj +++ b/Clario.Desktop/Clario.Desktop.csproj @@ -1,8 +1,6 @@  WinExe - net8.0 enable @@ -19,7 +17,10 @@ All + + + diff --git a/Clario.Desktop/Program.cs b/Clario.Desktop/Program.cs index ebe8654..d1e1a8f 100644 --- a/Clario.Desktop/Program.cs +++ b/Clario.Desktop/Program.cs @@ -1,6 +1,5 @@ using System; using Avalonia; -using Clario.Services; namespace Clario.Desktop; diff --git a/Clario.iOS/Clario.iOS.csproj b/Clario.iOS/Clario.iOS.csproj index ab5ee0b..f14564b 100644 --- a/Clario.iOS/Clario.iOS.csproj +++ b/Clario.iOS/Clario.iOS.csproj @@ -9,7 +9,10 @@ + + + diff --git a/Clario/App.axaml b/Clario/App.axaml index fc6cda3..223eabb 100644 --- a/Clario/App.axaml +++ b/Clario/App.axaml @@ -2,10 +2,12 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Clario" xmlns:converters="clr-namespace:Clario.Converters" + xmlns:views="clr-namespace:Clario.Views" + xmlns:vm="clr-namespace:Clario.ViewModels" + xmlns:styling="clr-namespace:FluentAvalonia.Styling;assembly=FluentAvalonia" x:Class="Clario.App" - RequestedThemeVariant="Dark"> + RequestedThemeVariant="Default"> - @@ -24,9 +26,12 @@ + + + \ No newline at end of file diff --git a/Clario/App.axaml.cs b/Clario/App.axaml.cs index bda3905..cfed3a8 100644 --- a/Clario/App.axaml.cs +++ b/Clario/App.axaml.cs @@ -2,11 +2,10 @@ using System; using System.Globalization; using Avalonia; using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Data.Core; using Avalonia.Data.Core.Plugins; using System.Linq; -using System.Threading; using Avalonia.Markup.Xaml; +using Avalonia.Styling; using Clario.Data; using Clario.Services; using Clario.ViewModels; @@ -16,15 +15,42 @@ namespace Clario; public partial class App : Application { + public static bool IsMobile { get; private set; } + public override void Initialize() { AvaloniaXamlLoader.Load(this); + RequestedThemeVariant = ThemeVariant.Dark; } public override async void OnFrameworkInitializationCompleted() { base.OnFrameworkInitializationCompleted(); + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLoading) + { + // Avoid duplicate validations from both Avalonia and the CommunityToolkit. + // More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins + DisableAvaloniaDataAnnotationValidation(); + + desktopLoading.MainWindow = new MainWindow + { + DataContext = new LoadingViewModel() + }; + desktopLoading.MainWindow.Show(); + } + + else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatformLoading) + { + Console.WriteLine("ANDROID PATH HIT"); + singleViewPlatformLoading.MainView = new MainAppMobile() + { + DataContext = new LoadingViewModel() + }; + } + + IsMobile = ApplicationLifetime is ISingleViewApplicationLifetime; + var culture = new CultureInfo("en-US"); CultureInfo.DefaultThreadCurrentCulture = culture; @@ -36,7 +62,6 @@ public partial class App : Application } catch { - /* session invalid or expired */ } var user = SupabaseService.Client.Auth.CurrentUser; @@ -53,19 +78,13 @@ public partial class App : Application // More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins DisableAvaloniaDataAnnotationValidation(); - desktop.MainWindow = new MainWindow - { - DataContext = user is not null ? new MainViewModel() : new AuthViewModel() - }; - desktop.MainWindow.Show(); + desktop.MainWindow!.DataContext = user is not null ? new MainViewModel() : new AuthViewModel(); } else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform) { - singleViewPlatform.MainView = new MainView - { - DataContext = user is not null ? new MainViewModel() : new AuthViewModel() - }; + Console.WriteLine("ANDROID PATH HIT"); + singleViewPlatform.MainView!.DataContext = user is not null ? new MainViewModel() : new AuthViewModel(); } } diff --git a/Clario/Assets/Icons/arrow-left-right.svg b/Clario/Assets/Icons/arrow-left-right.svg new file mode 100644 index 0000000..d98fbc9 --- /dev/null +++ b/Clario/Assets/Icons/arrow-left-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Clario/Assets/Icons/sliders-horizontal.svg b/Clario/Assets/Icons/sliders-horizontal.svg new file mode 100644 index 0000000..2dddc42 --- /dev/null +++ b/Clario/Assets/Icons/sliders-horizontal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Clario/Assets/Logo.png b/Clario/Assets/Logo.png new file mode 100644 index 0000000..609b617 Binary files /dev/null and b/Clario/Assets/Logo.png differ diff --git a/Clario/Clario.csproj b/Clario/Clario.csproj index 801406c..de6fc5e 100644 --- a/Clario/Clario.csproj +++ b/Clario/Clario.csproj @@ -22,7 +22,17 @@ All + + + + + + + MobileMainView.axaml + Code + + diff --git a/Clario/Clario.parcel b/Clario/Clario.parcel new file mode 100644 index 0000000..f024ba8 --- /dev/null +++ b/Clario/Clario.parcel @@ -0,0 +1,30 @@ +{ + "GeneralSettings": { + "NetProjectPath": "Clario.csproj", + "ApplicationName": "Clario", + "Version": "1.0.0", + "PackageName": { + "$type": "msbuild", + "property": "AssemblyName" + }, + "AssemblyName": { + "$type": "msbuild", + "property": "AssemblyName" + } + }, + "LinuxSettings": { + "CreateBinSymlink": "True" + }, + "Win32Settings": { + "IncludeUninstaller": "True" + }, + "MacOsSettings": { + "CreateBundle": true, + "BundleIdentifier": "com.CompanyName.Clario", + "SigningCredentialsType": "AdHoc" + }, + "PublishSettings": { + "PublishSingleFile": "True", + "ExtraBuildProperties": {} + } +} \ No newline at end of file diff --git a/Clario/Converters/AccountFromIdConverter.cs b/Clario/Converters/AccountFromIdConverter.cs index 172304b..1d3b1a8 100644 --- a/Clario/Converters/AccountFromIdConverter.cs +++ b/Clario/Converters/AccountFromIdConverter.cs @@ -11,7 +11,7 @@ public class AccountFromIdConverter : IValueConverter public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { if (value is not Guid) return null; - var accounts = DataRepo.General.Accounts; + var accounts = DataRepo.General.FetchAccounts().Result; if (accounts is null) return null; return accounts.FirstOrDefault(x => x.Id == (Guid)value)?.Name; } diff --git a/Clario/Converters/AmountSignConverter.cs b/Clario/Converters/AmountSignConverter.cs index 89fa418..56f9e1b 100644 --- a/Clario/Converters/AmountSignConverter.cs +++ b/Clario/Converters/AmountSignConverter.cs @@ -12,7 +12,11 @@ public class AmountSignConverter : IMultiValueConverter { if (values.Any(x => x is null) || values.Count < 2) return 0; if (values[0] is decimal amount && values[1] is string type) - return (type.Equals("income", StringComparison.CurrentCultureIgnoreCase) ? amount : -amount); + if (parameter is string param && param.Equals("round")) + return (type.Equals("income", StringComparison.CurrentCultureIgnoreCase) ? $"${Math.Round(amount)}" : $"-${Math.Round(amount)}"); + else + return (type.Equals("income", StringComparison.CurrentCultureIgnoreCase) ? $"${amount}" : $"-${amount}"); + return 0; } } \ No newline at end of file diff --git a/Clario/Converters/BoolToColorConverter.cs b/Clario/Converters/BoolToColorConverter.cs new file mode 100644 index 0000000..9b2efbb --- /dev/null +++ b/Clario/Converters/BoolToColorConverter.cs @@ -0,0 +1,33 @@ +using System; +using System.Globalization; +using Avalonia; +using Avalonia.Data.Converters; +using Avalonia.Media; + +namespace Clario.Converters; + +// BoolToColorConverter.cs +public class BoolToColorConverter : IValueConverter +{ + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is not bool boolValue || parameter is not string colors) + return AvaloniaProperty.UnsetValue; + + var parts = colors.Split('|'); + if (parts.Length != 2) return AvaloniaProperty.UnsetValue; + + var hex = boolValue ? parts[0] : parts[1]; + + if (targetType == typeof(IBrush) || targetType == typeof(SolidColorBrush)) + return SolidColorBrush.Parse(hex); + + if (targetType == typeof(Color)) + return Color.Parse(hex); + + return SolidColorBrush.Parse(hex); + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + => throw new NotImplementedException(); +} \ No newline at end of file diff --git a/Clario/Converters/BoolToCssConverter.cs b/Clario/Converters/BoolToCssConverter.cs new file mode 100644 index 0000000..30be2bb --- /dev/null +++ b/Clario/Converters/BoolToCssConverter.cs @@ -0,0 +1,25 @@ +using System; +using System.Globalization; +using Avalonia; +using Avalonia.Data.Converters; + +namespace Clario.Converters; + +// BoolToCssConverter.cs +public class BoolToCssConverter : IValueConverter +{ + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is not bool b || parameter is not string colors) + return AvaloniaProperty.UnsetValue; + + var parts = colors.Split('|'); + if (parts.Length != 2) return AvaloniaProperty.UnsetValue; + + var hex = b ? parts[0] : parts[1]; + return $"path, circle, rect, ellipse, line, polyline, polygon, text, use {{ stroke: {hex}; }}"; + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + => throw new NotImplementedException(); +} \ No newline at end of file diff --git a/Clario/Converters/DecimalColorConverter.cs b/Clario/Converters/DecimalColorConverter.cs index 077d702..7e23934 100644 --- a/Clario/Converters/DecimalColorConverter.cs +++ b/Clario/Converters/DecimalColorConverter.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using Avalonia.Data.Converters; -using Avalonia.Media; namespace Clario.Converters; diff --git a/Clario/Converters/DecimalSignConverter.cs b/Clario/Converters/DecimalSignConverter.cs index 663cfb9..8a5df7e 100644 --- a/Clario/Converters/DecimalSignConverter.cs +++ b/Clario/Converters/DecimalSignConverter.cs @@ -8,7 +8,8 @@ public class DecimalSignConverter : IValueConverter { public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { - if (value is decimal d) return (d < 0 ? $"-${Math.Abs(Math.Round(d))}" : $"+${Math.Abs(Math.Round(d))}"); + if (value is decimal d) + return (d < 0 ? $"-${Math.Abs(Math.Round(d))}" : $"+${Math.Abs(Math.Round(d))}"); return "$0"; } diff --git a/Clario/Converters/FirstValueConverter.cs b/Clario/Converters/FirstValueConverter.cs index 375ae6f..8a3d2ec 100644 --- a/Clario/Converters/FirstValueConverter.cs +++ b/Clario/Converters/FirstValueConverter.cs @@ -1,6 +1,5 @@ using System; using System.Globalization; -using System.Runtime.InteropServices.JavaScript; using Avalonia.Data.Converters; namespace Clario.Converters; diff --git a/Clario/Converters/NetworthSumConverter.cs b/Clario/Converters/NetworthSumConverter.cs index 8bdac89..447c1ca 100644 --- a/Clario/Converters/NetworthSumConverter.cs +++ b/Clario/Converters/NetworthSumConverter.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using Avalonia.Data.Converters; -using LiveChartsCore.SkiaSharpView.Avalonia; namespace Clario.Converters; diff --git a/Clario/Converters/PercentageConverter.cs b/Clario/Converters/PercentageConverter.cs index 1c9d68a..5d40a38 100644 --- a/Clario/Converters/PercentageConverter.cs +++ b/Clario/Converters/PercentageConverter.cs @@ -14,8 +14,8 @@ public class PercentageConverter : IMultiValueConverter if (value[0] is decimal part && value[1] is decimal total && part > 0) { - var percentage = Math.Round(part / total, 2); - return percentage.ToString("0%"); + var percentage = Math.Round(part / total, 3); + return percentage.ToString("0.0%"); } return "0%"; diff --git a/Clario/Converters/SvgPathFromName.cs b/Clario/Converters/SvgPathFromName.cs index 7b949b7..2f4c510 100644 --- a/Clario/Converters/SvgPathFromName.cs +++ b/Clario/Converters/SvgPathFromName.cs @@ -1,8 +1,6 @@ using System; using System.Globalization; -using System.Linq; using Avalonia.Data.Converters; -using Clario.Data; namespace Clario.Converters; diff --git a/Clario/CustomControls/DateRangePicker.axaml b/Clario/CustomControls/DateRangePicker.axaml index 228fe08..9c715a2 100644 --- a/Clario/CustomControls/DateRangePicker.axaml +++ b/Clario/CustomControls/DateRangePicker.axaml @@ -1,50 +1,84 @@  - - - - - + + + + + + + \ No newline at end of file diff --git a/Clario/CustomControls/DateRangePicker.cs b/Clario/CustomControls/DateRangePicker.cs index 203b63a..052cc60 100644 --- a/Clario/CustomControls/DateRangePicker.cs +++ b/Clario/CustomControls/DateRangePicker.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; +using Calendar = Avalonia.Controls.Calendar; namespace Clario.CustomControls; @@ -19,7 +21,6 @@ public class DateRangePicker : TemplatedControl set => SetValue(SelectionModeProperty, value); } - public static readonly StyledProperty> SelectedDatesProperty = AvaloniaProperty.Register>( nameof(SelectedDates), new List()); @@ -30,6 +31,16 @@ public class DateRangePicker : TemplatedControl set => SetValue(SelectedDatesProperty, value); } + public static readonly StyledProperty SelectedDateProperty = + AvaloniaProperty.Register( + nameof(SelectedDate), null); + + public DateTime? SelectedDate + { + get => GetValue(SelectedDateProperty); + set => SetValue(SelectedDateProperty, value); + } + public static readonly StyledProperty DisplayTextProperty = AvaloniaProperty.Register( nameof(DisplayText), "Select Date"); @@ -40,114 +51,180 @@ public class DateRangePicker : TemplatedControl set => SetValue(DisplayTextProperty, value); } - private Button _button; - private Popup _popup; - private Calendar _calendar; + + private Button? _button; + private Popup? _popup; + private Calendar? _calendar; - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - base.OnPropertyChanged(change); - if (change.Property == SelectedDatesProperty && _calendar != null) - { - _calendar.SelectedDates.Clear(); + private bool _isSyncing = false; - foreach (var date in SelectedDates) - { - _calendar.SelectedDates.Add(date); - } - - if (SelectionMode == CalendarSelectionMode.SingleDate) - _popup.IsOpen = false; - } - } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); + + + if (_button != null) _button.Click -= OnButtonClick; + if (_calendar != null) _calendar.SelectedDatesChanged -= OnCalendarDatesChanged; + _button = e.NameScope.Find + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Clario/MobileViews/AccountsViewMobile.axaml.cs b/Clario/MobileViews/AccountsViewMobile.axaml.cs new file mode 100644 index 0000000..0cb3dff --- /dev/null +++ b/Clario/MobileViews/AccountsViewMobile.axaml.cs @@ -0,0 +1,118 @@ +using System; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Animation.Easings; +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.Styling; +using Clario.Models; + +namespace Clario.MobileViews; + +public partial class AccountsViewMobile : UserControl +{ + private bool _sheetVisible = false; + + private TranslateTransform SheetTranslate => + (TranslateTransform)BottomSheet.RenderTransform!; + + public AccountsViewMobile() + { + InitializeComponent(); + + DimOverlay.PointerPressed += async (_, _) => await HideSheet(); + CloseButton.Click += async (_, _) => await HideSheet(); + + AddHandler(Button.ClickEvent, async (sender, e) => + { + if (e.Source is Button { DataContext: Account }) await ShowSheet(); + }, handledEventsToo: false); + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + BottomSheet.MaxHeight = Bounds.Height * 0.82; + + // update if screen size changes + PropertyChanged += (_, args) => + { + if (args.Property == BoundsProperty) + BottomSheet.MaxHeight = Bounds.Height * 0.82; + }; + } + + public async Task ShowSheet() + { + if (_sheetVisible) return; + _sheetVisible = true; + + OverlayGrid.IsVisible = true; + DimOverlay.Opacity = 0; + SheetTranslate.Y = 800; + + var sheetAnim = new Animation + { + Duration = TimeSpan.FromMilliseconds(320), + Easing = new CubicEaseOut(), + FillMode = FillMode.Forward, + Children = + { + new KeyFrame { Cue = new Cue(0d), Setters = { new Setter(TranslateTransform.YProperty, 800d) } }, + new KeyFrame { Cue = new Cue(1d), Setters = { new Setter(TranslateTransform.YProperty, 0d) } } + } + }; + + var dimAnim = new Animation + { + Duration = TimeSpan.FromMilliseconds(220), + FillMode = FillMode.Forward, + Children = + { + new KeyFrame { Cue = new Cue(0d), Setters = { new Setter(OpacityProperty, 0d) } }, + new KeyFrame { Cue = new Cue(1d), Setters = { new Setter(OpacityProperty, 1d) } } + } + }; + + await Task.WhenAll(sheetAnim.RunAsync(BottomSheet), dimAnim.RunAsync(DimOverlay)); + + SheetTranslate.Y = 0; + DimOverlay.Opacity = 1; + } + + public async Task HideSheet() + { + if (!_sheetVisible) return; + + var sheetAnim = new Animation + { + Duration = TimeSpan.FromMilliseconds(260), + Easing = new CubicEaseIn(), + FillMode = FillMode.Forward, + Children = + { + new KeyFrame { Cue = new Cue(0d), Setters = { new Setter(TranslateTransform.YProperty, 0d) } }, + new KeyFrame { Cue = new Cue(1d), Setters = { new Setter(TranslateTransform.YProperty, 800d) } } + } + }; + + var dimAnim = new Animation + { + Duration = TimeSpan.FromMilliseconds(200), + FillMode = FillMode.Forward, + Children = + { + new KeyFrame { Cue = new Cue(0d), Setters = { new Setter(OpacityProperty, 1d) } }, + new KeyFrame { Cue = new Cue(1d), Setters = { new Setter(OpacityProperty, 0d) } } + } + }; + + await Task.WhenAll(sheetAnim.RunAsync(BottomSheet), dimAnim.RunAsync(DimOverlay)); + + _sheetVisible = false; + OverlayGrid.IsVisible = false; + SheetTranslate.Y = 0; + DimOverlay.Opacity = 1; + } +} \ No newline at end of file diff --git a/Clario/MobileViews/BudgetViewMobile.axaml b/Clario/MobileViews/BudgetViewMobile.axaml new file mode 100644 index 0000000..3424b3b --- /dev/null +++ b/Clario/MobileViews/BudgetViewMobile.axaml @@ -0,0 +1,478 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Clario/MobileViews/BudgetViewMobile.axaml.cs b/Clario/MobileViews/BudgetViewMobile.axaml.cs new file mode 100644 index 0000000..e34ca91 --- /dev/null +++ b/Clario/MobileViews/BudgetViewMobile.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace Clario.MobileViews; + +public partial class BudgetViewMobile : UserControl +{ + public BudgetViewMobile() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Clario/MobileViews/DashboardViewMobile.axaml b/Clario/MobileViews/DashboardViewMobile.axaml new file mode 100644 index 0000000..6de6328 --- /dev/null +++ b/Clario/MobileViews/DashboardViewMobile.axaml @@ -0,0 +1,480 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Clario/MobileViews/MainViewMobile.axaml.cs b/Clario/MobileViews/MainViewMobile.axaml.cs new file mode 100644 index 0000000..28de597 --- /dev/null +++ b/Clario/MobileViews/MainViewMobile.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace Clario.MobileViews; + +public partial class MainViewMobile : UserControl +{ + public MainViewMobile() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Clario/MobileViews/TransactionsViewMobile.axaml b/Clario/MobileViews/TransactionsViewMobile.axaml new file mode 100644 index 0000000..366af4a --- /dev/null +++ b/Clario/MobileViews/TransactionsViewMobile.axaml @@ -0,0 +1,356 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Clario/MobileViews/TransactionsViewMobile.axaml.cs b/Clario/MobileViews/TransactionsViewMobile.axaml.cs new file mode 100644 index 0000000..4b20864 --- /dev/null +++ b/Clario/MobileViews/TransactionsViewMobile.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace Clario.MobileViews; + +public partial class TransactionsViewMobile : UserControl +{ + public TransactionsViewMobile() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Clario/Models/Account.cs b/Clario/Models/Account.cs index f1be948..2d20caf 100644 --- a/Clario/Models/Account.cs +++ b/Clario/Models/Account.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Newtonsoft.Json; using Supabase.Postgrest.Attributes; using Supabase.Postgrest.Models; @@ -23,7 +24,7 @@ public class Account : BaseModel [Column("currency")] public string Currency { get; set; } = "USD"; [Column("opening_balance")] public decimal OpeningBalance { get; set; } - public decimal CurrentBalance { get; set; } + [JsonIgnore] public decimal CurrentBalance { get; set; } [Column("credit_limit")] public decimal? CreditLimit { get; set; } @@ -36,13 +37,13 @@ public class Account : BaseModel [Column("icon")] public string Icon { get; set; } = string.Empty; [Column("color")] public string Color { get; set; } = string.Empty; - - public int TransactionsCount { get; set; } - public int IncomeTransactionsThisMonth { get; set; } - public int ExpenseTransactionsThisMonth { get; set; } - public decimal TotalIncomeThisMonth { get; set; } - public decimal TotalExpenseThisMonth { get; set; } - public decimal MonthlyIncrease { get; set; } - public List? RecentTransactions { get; set; } - public bool GroupHeader { get; set; } = false; + + [JsonIgnore] public int TransactionsCount { get; set; } + [JsonIgnore] public int IncomeTransactionsThisMonth { get; set; } + [JsonIgnore] public int ExpenseTransactionsThisMonth { get; set; } + [JsonIgnore] public decimal TotalIncomeThisMonth { get; set; } + [JsonIgnore] public decimal TotalExpenseThisMonth { get; set; } + [JsonIgnore] public decimal MonthlyIncrease { get; set; } + [JsonIgnore] public List? RecentTransactions { get; set; } + [JsonIgnore] public bool GroupHeader { get; set; } = false; } \ No newline at end of file diff --git a/Clario/Models/Budget.cs b/Clario/Models/Budget.cs new file mode 100644 index 0000000..0e2e89c --- /dev/null +++ b/Clario/Models/Budget.cs @@ -0,0 +1,50 @@ +using System; +using Newtonsoft.Json; +using Supabase.Postgrest.Attributes; +using Supabase.Postgrest.Models; + + +namespace Clario.Models; + +[Table("budgets")] +public class Budget : BaseModel +{ + [PrimaryKey("id", false)] public Guid Id { get; set; } + + [Column("user_id")] public Guid UserId { get; set; } + + [Column("category_id")] public Guid CategoryId { get; set; } + + [Column("amount")] public decimal LimitAmount { get; set; } + + [Column("period")] public string Period { get; set; } = "monthly"; + + [Column("alert_threshold")] public int AlertThreshold { get; set; } = 80; + + [Column("rollover")] public bool Rollover { get; set; } + + [Column("created_at")] public DateTime CreatedAt { get; set; } + + // ── not in DB ────────────────────────────────────── + + [JsonIgnore] public Category? Category { get; set; } + [JsonIgnore] public int TransactionsCount { get; set; } + [JsonIgnore] public decimal Spent { get; set; } // populated after joining with transactions + + [JsonIgnore] public decimal Remaining => LimitAmount - Spent; + [JsonIgnore] public double PercentageUsed => LimitAmount > 0 ? Math.Round((double)(Spent / LimitAmount), 2) : 0; + [JsonIgnore] public bool IsOverBudget => Spent > LimitAmount; + [JsonIgnore] public bool IsWarning => !IsOverBudget && PercentageUsed * 100 >= AlertThreshold; + [JsonIgnore] public bool IsOnTrack => !IsOverBudget && PercentageUsed * 100 < AlertThreshold; + + [JsonIgnore] public string SpentFormatted => $"${Spent:N0}"; + [JsonIgnore] public string AmountFormatted => $"of ${LimitAmount:N0}"; + [JsonIgnore] public string PercentageFormatted => $"{PercentageUsed:P0} used"; + + [JsonIgnore] + public string RemainingFormatted => IsOverBudget + ? $"${Math.Abs(Remaining):N0} over" + : $"${Remaining:N0} left"; + + [JsonIgnore] public bool GroupHeader { get; set; } = false; +} \ No newline at end of file diff --git a/Clario/Models/Category.cs b/Clario/Models/Category.cs index e4fb24a..2f5d15c 100644 --- a/Clario/Models/Category.cs +++ b/Clario/Models/Category.cs @@ -1,5 +1,4 @@ using System; -using CommunityToolkit.Mvvm.ComponentModel; using Supabase.Postgrest.Attributes; using Supabase.Postgrest.Models; diff --git a/Clario/Models/ColumnChartData.cs b/Clario/Models/ColumnChartData.cs new file mode 100644 index 0000000..03279e1 --- /dev/null +++ b/Clario/Models/ColumnChartData.cs @@ -0,0 +1,18 @@ +using System; +using CommunityToolkit.Mvvm.ComponentModel; +using LiveChartsCore.Kernel; +using LiveChartsCore.SkiaSharpView.Painting; +using Newtonsoft.Json; + +namespace Clario.Models; + +public partial class ColumnChartData : ObservableObject +{ + public Guid id; + [ObservableProperty] private string _name; + [ObservableProperty] private double[] _values; + [ObservableProperty] private SolidColorPaint _fill; + + + [JsonIgnore] public Func ToolTipFormatter => point => $"${point.Coordinate.PrimaryValue:N0}"; +} \ No newline at end of file diff --git a/Clario/Models/Profile.cs b/Clario/Models/Profile.cs index c8d2039..d932f06 100644 --- a/Clario/Models/Profile.cs +++ b/Clario/Models/Profile.cs @@ -1,5 +1,4 @@ using System; -using ExCSS; using Supabase.Postgrest.Attributes; using Supabase.Postgrest.Models; @@ -14,6 +13,7 @@ public class Profile : BaseModel [Column("currency")] public string Currency { get; set; } [Column("theme")] public string Theme { get; set; } [Column("language")] public string Language { get; set; } + [Column("savings_goal")] public decimal? SavingsGoal { get; set; } [Column("created_at")] public DateTime CreatedAt { get; set; } [Column("updated_at")] public DateTime UpdatedAt { get; set; } diff --git a/Clario/Models/Transaction.cs b/Clario/Models/Transaction.cs index 077f099..bba0cc6 100644 --- a/Clario/Models/Transaction.cs +++ b/Clario/Models/Transaction.cs @@ -1,7 +1,7 @@ using System; using System.Linq; using Clario.Data; -using CommunityToolkit.Mvvm.ComponentModel; +using Newtonsoft.Json; using Supabase.Postgrest.Attributes; using Supabase.Postgrest.Models; @@ -26,11 +26,12 @@ public class Transaction : BaseModel { _categoryId = value; - Category = DataRepo.General.Categories?.FirstOrDefault(x => x.Id == value); + Category = DataRepo.General.FetchCategories().Result.FirstOrDefault(x => x.Id == value); } } - public Category? Category { get; set; } + [JsonIgnore] public Category? Category { get; set; } + [Column("amount")] public decimal Amount { get; set; } [Column("type")] public string Type { get; set; } = string.Empty; // "income" or "expense" @@ -40,8 +41,8 @@ public class Transaction : BaseModel [Column("note")] public string? Note { get; set; } [Column("date")] public DateTime Date { get; set; } - + [Column("created_at")] public DateTime CreatedAt { get; set; } - public bool GroupHeader { get; set; } = false; + [JsonIgnore] public bool GroupHeader { get; set; } = false; } \ No newline at end of file diff --git a/Clario/Services/ThemeService.cs b/Clario/Services/ThemeService.cs index c35ee92..8da004b 100644 --- a/Clario/Services/ThemeService.cs +++ b/Clario/Services/ThemeService.cs @@ -1,7 +1,5 @@ -using System.Diagnostics; -using Avalonia; +using Avalonia; using Avalonia.Styling; -using CommunityToolkit.Mvvm.ComponentModel; namespace Clario.Services; diff --git a/Clario/Theme/AppTheme.axaml b/Clario/Theme/AppTheme.axaml index 8d5461a..9cf1f5c 100644 --- a/Clario/Theme/AppTheme.axaml +++ b/Clario/Theme/AppTheme.axaml @@ -11,7 +11,8 @@ - + + @@ -87,7 +88,6 @@ - @@ -463,8 +463,8 @@ + + + + + + + + + @@ -513,12 +545,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Clario/Theme/Styles/CalenderItemStyles.axaml b/Clario/Theme/Styles/CalenderItemStyles.axaml index 1033dca..0646a40 100644 --- a/Clario/Theme/Styles/CalenderItemStyles.axaml +++ b/Clario/Theme/Styles/CalenderItemStyles.axaml @@ -8,7 +8,7 @@