diff --git a/FinTrack/App.xaml.cs b/FinTrack/App.xaml.cs index f0d47c6..3be169c 100644 --- a/FinTrack/App.xaml.cs +++ b/FinTrack/App.xaml.cs @@ -1,13 +1,111 @@ -using System.Windows; +using CommunityToolkit.Mvvm.Messaging; +using FinTrack.Core; +using FinTrack.Services; +using FinTrack.Services.Api; +using FinTrack.ViewModels; +using FinTrack.Views; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Serilog; +using System.Windows; namespace FinTrack { public partial class App : Application { - protected override void OnStartup(StartupEventArgs e) + private readonly IHost _host; + + public App() + { + _host = Host.CreateDefaultBuilder() + .UseSerilog((context, services, configuration) => + { + configuration.ReadFrom.Configuration(context.Configuration); + }) + .ConfigureServices((context, services) => + { + ConfigureServices(services); + }) + .Build(); + + SetupGlobalExceptionHandling(); + } + + private void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(WeakReferenceMessenger.Default); + + services.AddSingleton(); + services.AddSingleton(); + + services.AddSingleton(); + services.AddSingleton(); + + services.AddTransient(); + services.AddTransient(); + + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + } + + protected override async void OnStartup(StartupEventArgs e) { + await _host.StartAsync(); + + var logger = _host.Services.GetRequiredService>(); + logger.LogInformation("Uygulama başlatıldı ve Host çalışıyor."); + + var window = _host.Services.GetRequiredService(); + window.Show(); + base.OnStartup(e); - new LoginWindow().Show(); + } + + private void SetupGlobalExceptionHandling() + { + DispatcherUnhandledException += (sender, e) => + { + var logger = _host.Services.GetRequiredService>(); + logger.LogCritical(e.Exception, "YAKALANAMAYAN UI HATASI!"); + + MessageBox.Show("Beklenmedik bir hata oluştu. Uygulama kapanacak. Detaylar log dosyasına yazıldı.", "Kritik Hata", MessageBoxButton.OK, MessageBoxImage.Error); + e.Handled = true; + Shutdown(); + }; + + AppDomain.CurrentDomain.UnhandledException += (sender, e) => + { + var logger = _host.Services.GetRequiredService>(); + logger.LogCritical(e.ExceptionObject as Exception, "YAKALANAMAYAN ARKA PLAN HATASI!"); + }; + } + + protected override async void OnExit(ExitEventArgs e) + { + using (_host) + { + await _host.StopAsync(TimeSpan.FromSeconds(5)); + } + base.OnExit(e); } } } \ No newline at end of file diff --git a/FinTrack/Core/ISecureTokenStorage.cs b/FinTrack/Core/ISecureTokenStorage.cs new file mode 100644 index 0000000..fbce6d5 --- /dev/null +++ b/FinTrack/Core/ISecureTokenStorage.cs @@ -0,0 +1,9 @@ +namespace FinTrack.Core +{ + public interface ISecureTokenStorage + { + void SaveToken(string token); + string? GetToken(); + void ClearToken(); + } +} \ No newline at end of file diff --git a/FinTrack/Core/SecureTokenStorage.cs b/FinTrack/Core/SecureTokenStorage.cs index bd44df2..5c154ed 100644 --- a/FinTrack/Core/SecureTokenStorage.cs +++ b/FinTrack/Core/SecureTokenStorage.cs @@ -4,7 +4,7 @@ namespace FinTrack.Core { - public class SecureTokenStorage + public class SecureTokenStorage : ISecureTokenStorage { private readonly string _filePath; diff --git a/FinTrack/Core/TokenValidator.cs b/FinTrack/Core/TokenValidator.cs new file mode 100644 index 0000000..dcc03cf --- /dev/null +++ b/FinTrack/Core/TokenValidator.cs @@ -0,0 +1,31 @@ +using System.IdentityModel.Tokens.Jwt; + +namespace FinTrack.Core +{ + public static class TokenValidator + { + public static bool IsTokenValid(string token) + { + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + var tokenHanler = new JwtSecurityTokenHandler(); + + try + { + var jwtToken = tokenHanler.ReadJwtToken(token); + + var expiration = jwtToken.ValidTo; + + return expiration > DateTime.UtcNow; + } + catch (Exception ex) + { + Console.WriteLine($"Token validation failed: {ex.Message}"); + return false; + } + } + } +} diff --git a/FinTrack/Dtos/UserProfileDto.cs b/FinTrack/Dtos/UserProfileDto.cs new file mode 100644 index 0000000..0a1d288 --- /dev/null +++ b/FinTrack/Dtos/UserProfileDto.cs @@ -0,0 +1,12 @@ +namespace FinTrack.Dtos +{ + public class UserProfileDto + { + public int Id { get; set; } + public string UserName { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public string ProfilePicture { get; set; } = string.Empty; + public string Role { get; set; } = string.Empty; + public string MembershipType { get; set; } = string.Empty; + } +} diff --git a/FinTrack/Enums/AccountType.cs b/FinTrack/Enums/AccountType.cs new file mode 100644 index 0000000..a765051 --- /dev/null +++ b/FinTrack/Enums/AccountType.cs @@ -0,0 +1,13 @@ +namespace FinTrack.Enums +{ + public enum AccountType + { + Checking, + Savings, + CreditCard, + Cash, + Investment, + Loan, + Other, + } // Kontrol, Tasarruf, Kredi Kartı, Nakit, Yatırım, Kredi, Diğer +} diff --git a/FinTrack/Enums/TransactionType.cs b/FinTrack/Enums/TransactionType.cs new file mode 100644 index 0000000..bdd9c06 --- /dev/null +++ b/FinTrack/Enums/TransactionType.cs @@ -0,0 +1,4 @@ +namespace FinTrack.Enums +{ + public enum TransactionType { Income, Expense } +} diff --git a/FinTrack/FinTrack.csproj b/FinTrack/FinTrack.csproj index e7b5c79..9a147b0 100644 --- a/FinTrack/FinTrack.csproj +++ b/FinTrack/FinTrack.csproj @@ -49,8 +49,14 @@ + + + + + + diff --git a/FinTrack/Messages/LoginSuccessMessage.cs b/FinTrack/Messages/LoginSuccessMessage.cs new file mode 100644 index 0000000..24f6258 --- /dev/null +++ b/FinTrack/Messages/LoginSuccessMessage.cs @@ -0,0 +1,4 @@ +namespace FinTrack.Messages +{ + public class LoginSuccessMessage { } +} diff --git a/FinTrack/Messages/NavigationMessage.cs b/FinTrack/Messages/NavigationMessage.cs new file mode 100644 index 0000000..3a4e8a4 --- /dev/null +++ b/FinTrack/Messages/NavigationMessage.cs @@ -0,0 +1,6 @@ +namespace FinTrack.Messages +{ + public class LoginSuccessMessag + { + } +} diff --git a/FinTrack/Models/AccountModel.cs b/FinTrack/Models/AccountModel.cs index a52a698..02e1960 100644 --- a/FinTrack/Models/AccountModel.cs +++ b/FinTrack/Models/AccountModel.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations; +using FinTrack.Enums; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace FinTrack.Models @@ -48,15 +49,4 @@ public class AccountModel public virtual ICollection Transactions { get; set; } = new List(); } - - public enum AccountType - { - Checking, - Savings, - CreditCard, - Cash, - Investment, - Loan, - Other, - } // Kontrol, Tasarruf, Kredi Kartı, Nakit, Yatırım, Kredi, Diğer } diff --git a/FinTrack/Models/Dashboard/AccountDashboard.cs b/FinTrack/Models/Dashboard/AccountDashboard.cs new file mode 100644 index 0000000..3657a8d --- /dev/null +++ b/FinTrack/Models/Dashboard/AccountDashboard.cs @@ -0,0 +1,12 @@ +using System.Windows.Media; + +namespace FinTrack.Models.Dashboard +{ + public class AccountDashboard + { + public string Name { get; set; } = string.Empty; + public double Percentage { get; set; } + public string Balance { get; set; } = string.Empty; + public Brush ProgressBarBrush { get; set; } = Brushes.Transparent; + } +} diff --git a/FinTrack/Models/Dashboard/BudgetDashboard.cs b/FinTrack/Models/Dashboard/BudgetDashboard.cs new file mode 100644 index 0000000..85331e2 --- /dev/null +++ b/FinTrack/Models/Dashboard/BudgetDashboard.cs @@ -0,0 +1,13 @@ +using System.Windows.Media; + +namespace FinTrack.Models.Dashboard +{ + public class BudgetDashboard + { + public string Name { get; set; } = string.Empty; + public string DueDate { get; set; } = string.Empty; + public string Amount { get; set; } = string.Empty; + public string RemainingTime { get; set; } = string.Empty; + public Brush StatusBrush { get; set; } = Brushes.Transparent; + } +} diff --git a/FinTrack/Models/Dashboard/CurrencyRateDashboard.cs b/FinTrack/Models/Dashboard/CurrencyRateDashboard.cs new file mode 100644 index 0000000..10cb703 --- /dev/null +++ b/FinTrack/Models/Dashboard/CurrencyRateDashboard.cs @@ -0,0 +1,15 @@ +namespace FinTrack.Models.Dashboard +{ + public class CurrencyRateDashboard + { + public string FromCurrencyFlagUrl { get; set; } = string.Empty; + public string FromCurrencyCountry { get; set; } = string.Empty; + public string FromCurrencyName { get; set; } = string.Empty; + public string FromCurrencyAmount { get; set; } = string.Empty; + public string ToCurrencyFlagUrl { get; set; } = string.Empty; + public string ToCurrencyCountry { get; set; } = string.Empty; + public string ToCurrencyName { get; set; } = string.Empty; + public string ToCurrencyAmount { get; set; } = string.Empty; + public double ToCurrencyImageHeight { get; set; } = 20; + } +} diff --git a/FinTrack/Models/Dashboard/DebtDashboard.cs b/FinTrack/Models/Dashboard/DebtDashboard.cs new file mode 100644 index 0000000..abeddbc --- /dev/null +++ b/FinTrack/Models/Dashboard/DebtDashboard.cs @@ -0,0 +1,16 @@ +namespace FinTrack.Models.Dashboard +{ + public class DebtDashboard + { + public string LenderName { get; set; } = string.Empty; + public string LenderIconPath { get; set; } = string.Empty; + public string BorrowerName { get; set; } = string.Empty; + public string BorrowerIconPath { get; set; } = string.Empty; + public string Status { get; set; } = string.Empty; + public string StatusBrush { get; set; } = string.Empty; + public string Amount { get; set; } = string.Empty; + public string CreationDate { get; set; } = string.Empty; + public string DueDate { get; set; } = string.Empty; + public string ReviewDate { get; set; } = string.Empty; + } +} diff --git a/FinTrack/Models/Dashboard/MembershipDashboard.cs b/FinTrack/Models/Dashboard/MembershipDashboard.cs new file mode 100644 index 0000000..088a499 --- /dev/null +++ b/FinTrack/Models/Dashboard/MembershipDashboard.cs @@ -0,0 +1,10 @@ +namespace FinTrack.Models.Dashboard +{ + public class MembershipDashboard + { + public string Level { get; set; } = string.Empty; + public string StartDate { get; set; } = string.Empty; + public string RenewalDate { get; set; } = string.Empty; + public string Price { get; set; } = string.Empty; + } +} diff --git a/FinTrack/Models/Dashboard/ReportDashboard.cs b/FinTrack/Models/Dashboard/ReportDashboard.cs new file mode 100644 index 0000000..9366a96 --- /dev/null +++ b/FinTrack/Models/Dashboard/ReportDashboard.cs @@ -0,0 +1,9 @@ +namespace FinTrack.Models.Dashboard +{ + public class ReportDashboard + { + public string Name { get; set; } = string.Empty; + public string IconPath { get; set; } = "/Assets/Icons/report.png"; + public string[] Formats { get; set; } = { "PDF", "WORD", "TEXT", "XML", "EXCEL", "MD" }; + } +} diff --git a/FinTrack/Models/Dashboard/TransactionDashboard.cs b/FinTrack/Models/Dashboard/TransactionDashboard.cs new file mode 100644 index 0000000..d8916d8 --- /dev/null +++ b/FinTrack/Models/Dashboard/TransactionDashboard.cs @@ -0,0 +1,15 @@ +using FinTrack.Enums; +using System.Windows.Media; + +namespace FinTrack.Models.Dashboard +{ + public class TransactionDashboard + { + public string DateText { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public string Amount { get; set; } = string.Empty; + public string Category { get; set; } = string.Empty; + public TransactionType Type { get; set; } = TransactionType.Expense; + public Brush DateBadgeBrush => Type == TransactionType.Income ? Brushes.Green : Brushes.Red; + } +} diff --git a/FinTrack/Services/Api/ApiService.cs b/FinTrack/Services/Api/ApiService.cs new file mode 100644 index 0000000..a8f511c --- /dev/null +++ b/FinTrack/Services/Api/ApiService.cs @@ -0,0 +1,196 @@ +using FinTrack.Core; +using Microsoft.Extensions.Logging; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.Json; + +namespace FinTrack.Services.Api +{ + public class ApiService : IApiService + { + private readonly string _baseUrl; + private readonly HttpClient _httpClient; + private readonly ILogger _logger; + private readonly JsonSerializerOptions _jsonSerializerOptions; + private readonly ISecureTokenStorage _secureTokenStorage; + + public ApiService(ILogger logger, ISecureTokenStorage secureTokenStorage) + { + _baseUrl = "http://localhost:5000/api/"; + _httpClient = new HttpClient + { + BaseAddress = new Uri(_baseUrl) + }; + _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + + _logger = logger; + _secureTokenStorage = secureTokenStorage; + + _jsonSerializerOptions = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }; + } + + private void AddAuthorizationHeader() + { + string token = _secureTokenStorage.GetToken() ?? "null"; + if (!string.IsNullOrEmpty(token)) + { + _httpClient.DefaultRequestHeaders.Authorization = null; + _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); + } + } + + public async Task DeleteAsync(string endpoint) + { + _logger.LogInformation("DELETE isteği başlatılıyor: {Endpoint}", endpoint); + if (string.IsNullOrWhiteSpace(endpoint)) + { + _logger.LogError("DELETE isteği sırasında endpoint boş veya null: {Endpoint}", endpoint); + throw new ArgumentException("Endpoint cannot be null or empty", nameof(endpoint)); + } + + try + { + AddAuthorizationHeader(); + + var response = await _httpClient.DeleteAsync(endpoint); + response.EnsureSuccessStatusCode(); + + var stream = await response.Content.ReadAsStreamAsync(); + var result = await JsonSerializer.DeserializeAsync(stream, _jsonSerializerOptions); + + _logger.LogInformation("DELETE isteği başarılı: {Endpoint}", endpoint); + return result; + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "DELETE isteği sırasında HTTP hatası oluştu: {Endpoint}. Status Code: {StatusCode}", endpoint, ex.StatusCode); + return default(T); + } + catch (Exception ex) + { + _logger.LogError(ex, "DELETE isteği sırasında genel bir hata oluştu: {Endpoint}", endpoint); + return default(T); + } + } + + public async Task GetAsync(string endpoint) + { + _logger.LogInformation("GET isteği başlatılıyor: {Endpoint}", endpoint); + try + { + AddAuthorizationHeader(); + + var response = await _httpClient.GetAsync(endpoint); + response.EnsureSuccessStatusCode(); + + var strean = await response.Content.ReadAsStreamAsync(); + var result = await JsonSerializer.DeserializeAsync(strean, _jsonSerializerOptions); + + _logger.LogInformation("GET isteği başarılı: {Endpoint}", endpoint); + return result; + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "GET isteği sırasında HTTP hatası oluştu: {Endpoint}. Status Code: {StatusCode}", endpoint, ex.StatusCode); + return default(T); + } + catch (JsonException ex) + { + _logger.LogError(ex, "GET isteği sırasında JSON serileştirme hatası oluştu: {Endpoint}", endpoint); + return default(T); + } + catch (Exception ex) + { + _logger.LogError(ex, "GET isteği sırasında beklenmeyen bir hata oluştu: {Endpoint}", endpoint); + return default(T); + } + } + + public async Task PostAsync(string endpoint, object data) + { + _logger.LogInformation("POST isteği başlatılıyor: {Endpoint}", endpoint); + if (string.IsNullOrWhiteSpace(endpoint)) + { + _logger.LogError("POST isteği sırasında endpoint boş veya null: {Endpoint}", endpoint); + throw new ArgumentException("Endpoint cannot be null or empty", nameof(endpoint)); + } + if (data == null) + { + _logger.LogError("POST isteği sırasında veri null: {Endpoint}", endpoint); + throw new ArgumentNullException(nameof(data), "Data cannot be null"); + } + + try + { + AddAuthorizationHeader(); + + var jsonPayload = JsonSerializer.Serialize(data); + var content = new StringContent(jsonPayload, System.Text.Encoding.UTF8, "application/json"); + + var response = await _httpClient.PostAsync(endpoint, content); + response.EnsureSuccessStatusCode(); + + var stream = await response.Content.ReadAsStreamAsync(); + var result = await JsonSerializer.DeserializeAsync(stream, _jsonSerializerOptions); + + _logger.LogInformation("POST isteği başarılı: {Endpoint}", endpoint); + return result; + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "POST isteği sırasında HTTP hatası oluştu: {Endpoint}. Status Code: {StatusCode}", endpoint, ex.StatusCode); + return default(T); + } + catch (Exception ex) + { + _logger.LogError(ex, "POST isteği sırasında genel bir hata oluştu: {Endpoint}", endpoint); + return default(T); + } + } + + public async Task PutAsync(string endpoint, object data) + { + _logger.LogInformation("PUT isteği başlatılıyor: {Endpoint}", endpoint); + if (string.IsNullOrWhiteSpace(endpoint)) + { + _logger.LogError("PUT isteği sırasında endpoint boş veya null: {Endpoint}", endpoint); + throw new ArgumentException("Endpoint cannot be null or empty", nameof(endpoint)); + } + if (data == null) + { + _logger.LogError("PUT isteği sırasında veri null: {Endpoint}", endpoint); + throw new ArgumentNullException(nameof(data), "Data cannot be null"); + } + + try + { + AddAuthorizationHeader(); + + var jsonPayload = JsonSerializer.Serialize(data); + var content = new StringContent(jsonPayload, System.Text.Encoding.UTF8, "application/json"); + + var response = await _httpClient.PutAsync(endpoint, content); + response.EnsureSuccessStatusCode(); + + var stream = await response.Content.ReadAsStreamAsync(); + var result = await JsonSerializer.DeserializeAsync(stream, _jsonSerializerOptions); + + _logger.LogInformation("PUT isteği başarılı: {Endpoint}", endpoint); + return result; + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "PUT isteği sırasında HTTP hatası oluştu: {Endpoint}. Status Code: {StatusCode}", endpoint, ex.StatusCode); + return default(T); + } + catch (Exception ex) + { + _logger.LogError(ex, "PUT isteği sırasında genel bir hata oluştu: {Endpoint}", endpoint); + return default(T); + } + } + } +} diff --git a/FinTrack/Services/Api/IApiService.cs b/FinTrack/Services/Api/IApiService.cs new file mode 100644 index 0000000..8d86baf --- /dev/null +++ b/FinTrack/Services/Api/IApiService.cs @@ -0,0 +1,10 @@ +namespace FinTrack.Services.Api +{ + public interface IApiService + { + Task GetAsync(string endpoint); + Task PostAsync(string endpoint, object data); + Task PutAsync(string endpoint, object data); + Task DeleteAsync(string endpoint); + } +} diff --git a/FinTrack/Services/AuthService.cs b/FinTrack/Services/AuthService.cs index 82f0b91..6bc8056 100644 --- a/FinTrack/Services/AuthService.cs +++ b/FinTrack/Services/AuthService.cs @@ -5,7 +5,7 @@ namespace FinTrack.Services { - public class AuthService + public class AuthService : IAuthService { private readonly HttpClient _httpClient; diff --git a/FinTrack/Services/IAuthService.cs b/FinTrack/Services/IAuthService.cs new file mode 100644 index 0000000..9417e99 --- /dev/null +++ b/FinTrack/Services/IAuthService.cs @@ -0,0 +1,10 @@ +namespace FinTrack.Services +{ + public interface IAuthService + { + Task LoginAsync(string email, string password); + void Logout(); + Task InitiateRegistrationAsnc(string userName, string email, string password); + Task VerifyOtpAndRegisterCodeAsync(string email, string code); + } +} diff --git a/FinTrack/Styles/ModernStyles.xaml b/FinTrack/Styles/ModernStyles.xaml index 43e97ab..36b48c5 100644 --- a/FinTrack/Styles/ModernStyles.xaml +++ b/FinTrack/Styles/ModernStyles.xaml @@ -66,14 +66,20 @@ - - + + - + + + + + + + @@ -129,7 +135,7 @@ - + @@ -534,25 +540,26 @@ + + + + + \ No newline at end of file diff --git a/FinTrack/ViewModels/AccountViewModel.cs b/FinTrack/ViewModels/AccountViewModel.cs new file mode 100644 index 0000000..7e7ecfc --- /dev/null +++ b/FinTrack/ViewModels/AccountViewModel.cs @@ -0,0 +1,8 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace FinTrack.ViewModels +{ + public partial class AccountViewModel : ObservableObject + { + } +} diff --git a/FinTrack/ViewModels/AuthenticatorViewModel.cs b/FinTrack/ViewModels/AuthenticatorViewModel.cs new file mode 100644 index 0000000..507db44 --- /dev/null +++ b/FinTrack/ViewModels/AuthenticatorViewModel.cs @@ -0,0 +1,46 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace FinTrack.ViewModels +{ + public partial class AuthenticatorViewModel : ObservableObject + { + [ObservableProperty] + private ObservableObject _currentViewModel; + + private readonly LoginViewModel _loginViewModel; + private readonly RegisterViewModel _registerViewModel; + private readonly OtpVerificationViewModel _otpVerificationViewModel; + private readonly ForgotPasswordViewModel _forgotPasswordViewModel; + private readonly ApplicationRecognizeSlideViewModel _applicationRecognizeSlideViewModel; + + public AuthenticatorViewModel( + LoginViewModel loginViewModel, + RegisterViewModel registerViewModel, + OtpVerificationViewModel otpVerificationViewModel, + ForgotPasswordViewModel forgotPasswordViewModel, + ApplicationRecognizeSlideViewModel applicationRecognizeSlideViewModel) + { + _loginViewModel = loginViewModel; + _registerViewModel = registerViewModel; + _otpVerificationViewModel = otpVerificationViewModel; + _forgotPasswordViewModel = forgotPasswordViewModel; + _applicationRecognizeSlideViewModel = applicationRecognizeSlideViewModel; + _currentViewModel = _applicationRecognizeSlideViewModel; + + _applicationRecognizeSlideViewModel.NavigateToLoginRequested += () => CurrentViewModel = _loginViewModel; + + _loginViewModel.NavigateToRegisterRequested += () => CurrentViewModel = _registerViewModel; + _loginViewModel.NavigateToForgotPasswordRequested += () => CurrentViewModel = _forgotPasswordViewModel; + + _registerViewModel.NavigateToLoginRequested += () => CurrentViewModel = _loginViewModel; + _registerViewModel.NavigateToOtpVerificationRequested += () => CurrentViewModel = _otpVerificationViewModel; + + _otpVerificationViewModel.NavigateToLoginRequested += () => CurrentViewModel = _loginViewModel; + _otpVerificationViewModel.NavigateToRegisterRequested += () => CurrentViewModel = _registerViewModel; + + _forgotPasswordViewModel.NavigateToLoginRequested += () => CurrentViewModel = _loginViewModel; + + CurrentViewModel = _applicationRecognizeSlideViewModel; + } + } +} \ No newline at end of file diff --git a/FinTrack/ViewModels/BottomBarViewModel.cs b/FinTrack/ViewModels/BottomBarViewModel.cs index 5f9f7f4..b000f3b 100644 --- a/FinTrack/ViewModels/BottomBarViewModel.cs +++ b/FinTrack/ViewModels/BottomBarViewModel.cs @@ -1,6 +1,8 @@ -namespace FinTrack.ViewModels +using CommunityToolkit.Mvvm.ComponentModel; + +namespace FinTrack.ViewModels { - internal class BottomBarViewModel + public partial class BottomBarViewModel : ObservableObject { } } diff --git a/FinTrack/ViewModels/BudgetViewModel.cs b/FinTrack/ViewModels/BudgetViewModel.cs new file mode 100644 index 0000000..42547f5 --- /dev/null +++ b/FinTrack/ViewModels/BudgetViewModel.cs @@ -0,0 +1,8 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace FinTrack.ViewModels +{ + public partial class BudgetViewModel : ObservableObject + { + } +} diff --git a/FinTrack/ViewModels/CurrenciesViewModel.cs b/FinTrack/ViewModels/CurrenciesViewModel.cs new file mode 100644 index 0000000..6190991 --- /dev/null +++ b/FinTrack/ViewModels/CurrenciesViewModel.cs @@ -0,0 +1,8 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace FinTrack.ViewModels +{ + public partial class CurrenciesViewModel : ObservableObject + { + } +} diff --git a/FinTrack/ViewModels/DashboardViewModel.cs b/FinTrack/ViewModels/DashboardViewModel.cs index 90fea8b..0c758ae 100644 --- a/FinTrack/ViewModels/DashboardViewModel.cs +++ b/FinTrack/ViewModels/DashboardViewModel.cs @@ -1,6 +1,114 @@ -namespace FinTrack.ViewModels +using CommunityToolkit.Mvvm.ComponentModel; +using FinTrack.Models.Dashboard; +using Microsoft.Extensions.Logging; +using System.Collections.ObjectModel; +using System.Windows; +using System.Windows.Media; + +namespace FinTrack.ViewModels { - internal class DashboardViewModel + public partial class DashboardViewModel : ObservableObject { + [ObservableProperty] + private ObservableCollection _budgets_DashboardView_ItemsControl; + + [ObservableProperty] + private ObservableCollection _currencyRates_DashboardView_ItemsControl; + + [ObservableProperty] + private ObservableCollection _accounts_DashboardView_ItemsControl; + + [ObservableProperty] + private string _totalBalance_DashboardView_TextBlock; + + [ObservableProperty] + private ObservableCollection _transactions_DashboardView_ListView; + + [ObservableProperty] + private MembershipDashboard _currentMembership_DashboardView_Multiple; + + [ObservableProperty] + private DebtDashboard _currentDebt_DashboardView_Multiple; + + [ObservableProperty] + private ObservableCollection _reports_DashboardView_ItemsControl; + + private readonly ILogger _logger; + + public DashboardViewModel(ILogger logger) + { + _logger = logger; + LoadData(); + } + + private void LoadData() + { + // [TEST] + Budgets_DashboardView_ItemsControl = new ObservableCollection + { + new BudgetDashboard { Name = "Tasarruf", DueDate = "15.02.2025", Amount = "6.000$", RemainingTime = "15 gün kaldı", StatusBrush = (Brush)Application.Current.FindResource("StatusGreenBrush") }, + new BudgetDashboard { Name = "Harcama", DueDate = "20.02.2025", Amount = "2.000$", RemainingTime = "10 gün kaldı", StatusBrush = (Brush)Application.Current.FindResource("StatusRedBrush") }, + new BudgetDashboard { Name = "Yatırım", DueDate = "10.03.2025", Amount = "10.000$", RemainingTime = "30 gün kaldı", StatusBrush = (Brush)Application.Current.FindResource("StatusGreenBrush") }, + new BudgetDashboard { Name = "Eğlence", DueDate = "05.04.2025", Amount = "8.000$", RemainingTime = "20 gün kaldı", StatusBrush = (Brush)Application.Current.FindResource("StatusGreenBrush") } + }; + + // [TEST] + CurrencyRates_DashboardView_ItemsControl = new ObservableCollection + { + new CurrencyRateDashboard + { + FromCurrencyFlagUrl = "https://flagcdn.com/w320/us.png", FromCurrencyCountry = "Amerika Birleşik Devletleri", FromCurrencyName = "Dolar", FromCurrencyAmount = "1.00", + ToCurrencyFlagUrl = "https://flagcdn.com/w320/tr.png", ToCurrencyCountry = "Türkiye", ToCurrencyName = "Türk Lirası", ToCurrencyAmount = "27.50", ToCurrencyImageHeight = 20 + }, + new CurrencyRateDashboard + { + FromCurrencyFlagUrl = "https://flagcdn.com/w320/eu.png", FromCurrencyCountry = "Euro Bölgesi", FromCurrencyName = "Euro",FromCurrencyAmount = "1.00", + ToCurrencyFlagUrl = "https://flagcdn.com/w320/tr.png", ToCurrencyCountry = "Türkiye", ToCurrencyName = "Türk Lirası", ToCurrencyAmount = "30.00", ToCurrencyImageHeight = 20 + } + }; + + // [TEST] + Accounts_DashboardView_ItemsControl = new ObservableCollection + { + new AccountDashboard { Name = "Nakit", Percentage = 50, Balance = "5.000$", ProgressBarBrush = (Brush)Application.Current.FindResource("StatusGreenBrush") }, + new AccountDashboard { Name = "Banka", Percentage = 30, Balance = "3.000$", ProgressBarBrush = (Brush)Application.Current.FindResource("StatusGreenBrush") } + }; + + TotalBalance_DashboardView_TextBlock = "7.000$"; + + // [TEST] + Transactions_DashboardView_ListView = new ObservableCollection + { + new TransactionDashboard { DateText = "01.01.2025", Description = "Market Alışverişi", Amount = "-150$", Category = "Gıda", Type = Enums.TransactionType.Expense }, + new TransactionDashboard { DateText = "02.01.2025", Description = "Maaş", Amount = "+3.000$", Category = "Gelir", Type = Enums.TransactionType.Income }, + new TransactionDashboard { DateText = "03.01.2025", Description = "Elektrik Faturası", Amount = "-200$", Category = "Fatura", Type = Enums.TransactionType.Expense } + }; + + // [TEST] + CurrentMembership_DashboardView_Multiple = new MembershipDashboard { Level = "Pro | AKTF", StartDate = "01.01.2025", RenewalDate = "01.02.2025", Price = "9.99$" }; + + // [TEST] + CurrentDebt_DashboardView_Multiple = new DebtDashboard + { + LenderName = "Ali Veli", + LenderIconPath = "https://i.pinimg.com/236x/be/a3/49/bea3491915571d34a026753f4a872000.jpg", + BorrowerName = "Ahmet Mehmet", + BorrowerIconPath = "https://pbs.twimg.com/profile_images/1144861916734451712/D76C3ugh_400x400.jpg", + Status = "Ödenmemiş", + StatusBrush = "StatusGreenBrush", + Amount = "1.000$", + CreationDate = "01.01.2025", + DueDate = "01.02.2025", + ReviewDate = "01.03.2025" + }; + + // [TEST] + Reports_DashboardView_ItemsControl = new ObservableCollection + { + new ReportDashboard { Name = "Gelir Raporu" }, + new ReportDashboard { Name = "Gider Raporu" }, + new ReportDashboard { Name = "Bütçe Raporu" } + }; + } } } diff --git a/FinTrack/ViewModels/HomeViewModel.cs b/FinTrack/ViewModels/DebtViewModel.cs similarity index 61% rename from FinTrack/ViewModels/HomeViewModel.cs rename to FinTrack/ViewModels/DebtViewModel.cs index b46375a..ad9ed1a 100644 --- a/FinTrack/ViewModels/HomeViewModel.cs +++ b/FinTrack/ViewModels/DebtViewModel.cs @@ -2,7 +2,7 @@ namespace FinTrack.ViewModels { - public partial class HomeViewModel : ObservableObject + public partial class DebtViewModel : ObservableObject { } } diff --git a/FinTrack/ViewModels/FeedbackViewModel.cs b/FinTrack/ViewModels/FeedbackViewModel.cs new file mode 100644 index 0000000..71aee5e --- /dev/null +++ b/FinTrack/ViewModels/FeedbackViewModel.cs @@ -0,0 +1,8 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace FinTrack.ViewModels +{ + public partial class FeedbackViewModel : ObservableObject + { + } +} diff --git a/FinTrack/ViewModels/FinBotViewModel.cs b/FinTrack/ViewModels/FinBotViewModel.cs new file mode 100644 index 0000000..8964c81 --- /dev/null +++ b/FinTrack/ViewModels/FinBotViewModel.cs @@ -0,0 +1,8 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace FinTrack.ViewModels +{ + public partial class FinBotViewModel : ObservableObject + { + } +} diff --git a/FinTrack/ViewModels/LoginViewModel.cs b/FinTrack/ViewModels/LoginViewModel.cs index 825189e..15f7735 100644 --- a/FinTrack/ViewModels/LoginViewModel.cs +++ b/FinTrack/ViewModels/LoginViewModel.cs @@ -1,7 +1,10 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; using FinTrack.Core; +using FinTrack.Messages; using FinTrack.Services; +using Microsoft.Extensions.Logging; using System.Windows; namespace FinTrack.ViewModels @@ -23,32 +26,53 @@ public partial class LoginViewModel : ObservableObject public event Action? NavigateToRegisterRequested; public event Action? NavigateToForgotPasswordRequested; - private readonly AuthService _authService; + private readonly IAuthService _authService; + private readonly ISecureTokenStorage _secureTokenStorage; - public LoginViewModel() - { - _authService = new AuthService(); + private readonly ILogger _logger; - RegisteredTokenLogin(); + public LoginViewModel( + IAuthService authService, + ILogger logger, + ISecureTokenStorage secureTokenStorage) + { + _authService = authService; + _logger = logger; + _secureTokenStorage = secureTokenStorage; + SavedTokenLogin(); } - private void RegisteredTokenLogin() + private void SavedTokenLogin() { - SecureTokenStorage secureTokenStorage = new SecureTokenStorage(); - string? token = secureTokenStorage.GetToken(); + string? token = _secureTokenStorage.GetToken(); if (!string.IsNullOrEmpty(token)) { + bool isValid = TokenValidator.IsTokenValid(token); + if (!isValid) + { + MessageBox.Show("Token geçersiz. Lütfen tekrar giriş yapın.", "Hata", MessageBoxButton.OK, MessageBoxImage.Error); + _logger.LogWarning("Geçersiz token bulundu. Kullanıcıdan yeni giriş yapması istendi."); + SessionManager.ClearToken(); + _secureTokenStorage.ClearToken(); + } + SessionManager.SetToken(token); MessageBox.Show("Giriş başarılı! Token kullanıldı.", "Bilgi", MessageBoxButton.OK, MessageBoxImage.Information); + _logger.LogInformation("Kullanıcı zaten giriş yapmış. Token kullanıldı."); + WeakReferenceMessenger.Default.Send(new LoginSuccessMessage()); } } [RelayCommand] private async Task Login_LoginView_Button() { + _logger.LogInformation("Kullanıcı giriş yapmaya çalışıyor. E-posta: {Email}", Email_LoginView_TextBox); + + WeakReferenceMessenger.Default.Send(new LoginSuccessMessage()); // TODO: [TEST] if (string.IsNullOrEmpty(Email_LoginView_TextBox) || string.IsNullOrEmpty(Password_LoginView_TextBox)) { MessageBox.Show("Lütfen e-posta ve şifre alanlarını doldurun.", "Hata", MessageBoxButton.OK, MessageBoxImage.Error); + _logger.LogWarning("Kullanıcı giriş yapmaya çalıştı ancak e-posta veya şifre alanları boş."); return; } @@ -56,15 +80,18 @@ private async Task Login_LoginView_Button() if (string.IsNullOrEmpty(token)) { MessageBox.Show("Giriş başarısız oldu. Lütfen e-posta ve şifrenizi kontrol edin.", "Hata", MessageBoxButton.OK, MessageBoxImage.Error); + _logger.LogError("Kullanıcı giriş yapmaya çalıştı ancak token alınamadı. E-posta veya şifre hatalı olabilir."); return; } SessionManager.SetToken(token); - SecureTokenStorage secureTokenStorage = new SecureTokenStorage(); - secureTokenStorage.SaveToken(token); + _secureTokenStorage.SaveToken(token); MessageBox.Show("Giriş başarılı!", "Bilgi", MessageBoxButton.OK, MessageBoxImage.Information); + _logger.LogInformation("Kullanıcı giriş yaptı ve token kaydedildi."); + + WeakReferenceMessenger.Default.Send(new LoginSuccessMessage()); } [RelayCommand] @@ -75,18 +102,21 @@ private void TogglePasswordVisibility_LoginView_Button() EyeIconSource_LoginView_Image = IsPasswordVisible_LoginView_PasswordBoxAndTextBox ? "/Assets/Images/Icons/eyeopen.png" : "/Assets/Images/Icons/eyeclose.png"; + _logger.LogInformation("Şifre görünürlüğü değiştirildi. Şifre {0}.", IsPasswordVisible_LoginView_PasswordBoxAndTextBox ? "görünür" : "gizli"); } [RelayCommand] private void NavigateToRegister_LoginView_Button() { NavigateToRegisterRequested?.Invoke(); + _logger.LogInformation("Kullanıcı kayıt sayfasına yönlendirildi."); } [RelayCommand] private void NavigateToForgotPassword_LoginView_Button() { NavigateToForgotPasswordRequested?.Invoke(); + _logger.LogInformation("Kullanıcı şifremi unuttum sayfasına yönlendirildi."); } } } \ No newline at end of file diff --git a/FinTrack/ViewModels/MainViewModel.cs b/FinTrack/ViewModels/MainViewModel.cs index 0af0136..06957f8 100644 --- a/FinTrack/ViewModels/MainViewModel.cs +++ b/FinTrack/ViewModels/MainViewModel.cs @@ -5,36 +5,72 @@ namespace FinTrack.ViewModels public partial class MainViewModel : ObservableObject { [ObservableProperty] - private ObservableObject _currentViewModel; + private ObservableObject currentCenterViewModel; - private readonly LoginViewModel _loginViewModel; - private readonly RegisterViewModel _registerViewModel; - private readonly OtpVerificationViewModel _otpVerificationViewModel; - private readonly ForgotPasswordViewModel _forgotPasswordViewModel; - private readonly ApplicationRecognizeSlideViewModel _applicationRecognizeSlideViewModel; - - public MainViewModel() - { - _loginViewModel = new LoginViewModel(); - _registerViewModel = new RegisterViewModel(); - _otpVerificationViewModel = new OtpVerificationViewModel(); - _forgotPasswordViewModel = new ForgotPasswordViewModel(); - _applicationRecognizeSlideViewModel = new ApplicationRecognizeSlideViewModel(); + [ObservableProperty] + private TopBarViewModel topBarViewModel; - _applicationRecognizeSlideViewModel.NavigateToLoginRequested += () => CurrentViewModel = _loginViewModel; + [ObservableProperty] + private BottomBarViewModel bottomBarViewModel; - _loginViewModel.NavigateToRegisterRequested += () => CurrentViewModel = _registerViewModel; - _loginViewModel.NavigateToForgotPasswordRequested += () => CurrentViewModel = _forgotPasswordViewModel; + private readonly DashboardViewModel _dashboardViewModel; + private readonly BudgetViewModel _budgetViewModel; + private readonly AccountViewModel _accountViewModel; + private readonly TransactionsViewModel _transactionsViewModel; + private readonly DebtViewModel _debtViewModel; + private readonly CurrenciesViewModel _currenciesViewModel; + private readonly ReportsViewModel _reportsViewModel; + private readonly FinBotViewModel _finBotViewModel; + private readonly FeedbackViewModel _feedbackViewModel; + private readonly SettingsViewModel _settingsViewModel; + private readonly NotificationViewModel _notificationViewModel; - _registerViewModel.NavigateToLoginRequested += () => CurrentViewModel = _loginViewModel; - _registerViewModel.NavigateToOtpVerificationRequested += () => CurrentViewModel = _otpVerificationViewModel; + public MainViewModel( + TopBarViewModel topBarViewModel, + BottomBarViewModel bottomBarViewModel, + DashboardViewModel dashboardViewModel, + BudgetViewModel budgetViewModel, + AccountViewModel accountViewModel, + TransactionsViewModel transactionsViewModel, + CurrenciesViewModel currenciesViewModel, + ReportsViewModel reportsViewModel, + DebtViewModel debtViewModel, + FinBotViewModel finBotViewModel, + FeedbackViewModel feedbackViewModel, + SettingsViewModel settingsViewModel, + NotificationViewModel notificationViewModel + ) + { + this.topBarViewModel = topBarViewModel; + this.bottomBarViewModel = bottomBarViewModel; - _otpVerificationViewModel.NavigateToLoginRequested += () => CurrentViewModel = _loginViewModel; - _otpVerificationViewModel.NavigateToRegisterRequested += () => CurrentViewModel = _registerViewModel; + _dashboardViewModel = dashboardViewModel; + _budgetViewModel = budgetViewModel; + _accountViewModel = accountViewModel; + _transactionsViewModel = transactionsViewModel; + _currenciesViewModel = currenciesViewModel; + _reportsViewModel = reportsViewModel; + _debtViewModel = debtViewModel; + _finBotViewModel = finBotViewModel; + _feedbackViewModel = feedbackViewModel; + _settingsViewModel = settingsViewModel; + _notificationViewModel = notificationViewModel; - _forgotPasswordViewModel.NavigateToLoginRequested += () => CurrentViewModel = _loginViewModel; + topBarViewModel.NavigateToDashboardRequested += () => CurrentCenterViewModel = _dashboardViewModel; + topBarViewModel.NavigateToAccountRequested += () => CurrentCenterViewModel = _accountViewModel; + topBarViewModel.NavigateToBudegtRequested += () => CurrentCenterViewModel = _budgetViewModel; + topBarViewModel.NavigateToTransactionsRequested += () => CurrentCenterViewModel = _transactionsViewModel; + topBarViewModel.NavigateToCurrenciesRequested += () => CurrentCenterViewModel = _currenciesViewModel; + topBarViewModel.NavigateToReportsRequested += () => CurrentCenterViewModel = _reportsViewModel; + topBarViewModel.NavigateToDebtRequested += () => CurrentCenterViewModel = _debtViewModel; + topBarViewModel.NavigateToFinBotRequested += () => CurrentCenterViewModel = _finBotViewModel; + topBarViewModel.NavigateToFeedbackRequested += () => CurrentCenterViewModel = _feedbackViewModel; + topBarViewModel.NavigateToSettingsRequested += () => CurrentCenterViewModel = _settingsViewModel; + topBarViewModel.NavigateToNotificationRequested += () => CurrentCenterViewModel = _notificationViewModel; - CurrentViewModel = _applicationRecognizeSlideViewModel; + TopBarViewModel = topBarViewModel; + CurrentCenterViewModel = _dashboardViewModel; + BottomBarViewModel = bottomBarViewModel; } } -} \ No newline at end of file +} diff --git a/FinTrack/ViewModels/NotificationViewModel.cs b/FinTrack/ViewModels/NotificationViewModel.cs new file mode 100644 index 0000000..dda216c --- /dev/null +++ b/FinTrack/ViewModels/NotificationViewModel.cs @@ -0,0 +1,8 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace FinTrack.ViewModels +{ + public partial class NotificationViewModel : ObservableObject + { + } +} diff --git a/FinTrack/ViewModels/OtpVerificationViewModel.cs b/FinTrack/ViewModels/OtpVerificationViewModel.cs index 5abc994..d8218f1 100644 --- a/FinTrack/ViewModels/OtpVerificationViewModel.cs +++ b/FinTrack/ViewModels/OtpVerificationViewModel.cs @@ -2,6 +2,7 @@ using CommunityToolkit.Mvvm.Input; using FinTrack.Core; using FinTrack.Services; +using Microsoft.Extensions.Logging; using System.Windows; namespace FinTrack.ViewModels @@ -18,20 +19,25 @@ public partial class OtpVerificationViewModel : ObservableObject public event Action? NavigateToRegisterRequested; - private readonly AuthService _authService; private int _counter; - public OtpVerificationViewModel() + private readonly IAuthService _authService; + private readonly ILogger _logger; + + public OtpVerificationViewModel(IAuthService authService, ILogger logger) { - _authService = new AuthService(); + _authService = authService; + _logger = logger; if (NewUserInformationManager.Email != null) { StartCounter(); + _logger.LogInformation("OTP doğrulama başlatıldı. E-posta: {Email}", NewUserInformationManager.Email); } else { MessageBox.Show("Lütfen önce kayıt işlemini tamamlayın.", "Hata", MessageBoxButton.OK, MessageBoxImage.Error); + _logger.LogWarning("OTP doğrulama için e-posta bilgisi eksik. Kullanıcı kayıt sayfasına yönlendiriliyor."); NavigateToRegisterRequested?.Invoke(); } } @@ -58,6 +64,7 @@ private async Task VerifyOtpCode_OtpVerificationView_Button() if (string.IsNullOrWhiteSpace(VerificationCode_OtpVerificationView_TextBox) || VerificationCode_OtpVerificationView_TextBox.Length != 6) { MessageBox.Show("Lütfen geçerli bir OTP kodu girin (6 haneli).", "Error", MessageBoxButton.OK, MessageBoxImage.Error); + _logger.LogWarning("Geçersiz OTP kodu girişi. Kullanıcıdan tekrar denemesi istendi."); return; } @@ -65,9 +72,11 @@ private async Task VerifyOtpCode_OtpVerificationView_Button() if (!isVerify) { MessageBox.Show("OTP doğrulama başarısız oldu. Lütfen kodu kontrol edin ve tekrar deneyin.", "Hata", MessageBoxButton.OK, MessageBoxImage.Error); + _logger.LogError("OTP doğrulama başarısız. E-posta: {Email}, Kod: {Code}", NewUserInformationManager.Email, VerificationCode_OtpVerificationView_TextBox); return; } MessageBox.Show("OTP doğrulama başarılı! Hoş geldiniz!", "Bilgi", MessageBoxButton.OK, MessageBoxImage.Information); + _logger.LogInformation("OTP doğrulama başarılı. Kullanıcı kaydı tamamlandı. E-posta: {Email}", NewUserInformationManager.Email); NewUserInformationManager.FullName = null; // Clear the stored user information NewUserInformationManager.Email = null; // Clear the stored user information @@ -89,13 +98,16 @@ private async Task CodeNotFound_OtpVerificationView_Button() if (!isInitiateRegistration) { MessageBox.Show("Kayıt işlemi başarısız oldu. Lütfen daha sonra tekrar deneyin.", "Hata", MessageBoxButton.OK, MessageBoxImage.Error); + _logger.LogError("Kayıt işlemi başarısız. E-posta: {Email}", NewUserInformationManager.Email); return; } MessageBox.Show("Doğrulama kodu tekrar gönderildi.", "Bilgi", MessageBoxButton.OK, MessageBoxImage.Information); + _logger.LogInformation("Doğrulama kodu tekrar gönderildi. E-posta: {Email}", NewUserInformationManager.Email); } else { MessageBox.Show("Kod gönderirken bir sorun oluştu. Bilgileri doğru giriniz.", "Hata", MessageBoxButton.OK, MessageBoxImage.Error); + _logger.LogWarning("Kod gönderme sırasında eksik kullanıcı bilgileri. Kullanıcı kayıt sayfasına yönlendiriliyor."); NavigateToRegisterRequested?.Invoke(); } } diff --git a/FinTrack/ViewModels/RegisterViewModel.cs b/FinTrack/ViewModels/RegisterViewModel.cs index ccbc51f..3f7babb 100644 --- a/FinTrack/ViewModels/RegisterViewModel.cs +++ b/FinTrack/ViewModels/RegisterViewModel.cs @@ -2,6 +2,7 @@ using CommunityToolkit.Mvvm.Input; using FinTrack.Core; using FinTrack.Services; +using Microsoft.Extensions.Logging; using System.Windows; namespace FinTrack.ViewModels @@ -26,11 +27,13 @@ public partial class RegisterViewModel : ObservableObject public event Action? NavigateToLoginRequested; public event Action? NavigateToOtpVerificationRequested; - private readonly AuthService _authService; + private readonly ILogger _logger; + private readonly IAuthService _authService; - public RegisterViewModel() + public RegisterViewModel(ILogger logger, IAuthService authService) { - _authService = new AuthService(); + _logger = logger; + _authService = authService; } [RelayCommand] @@ -42,6 +45,7 @@ private async Task Register_RegisterView_Button() string.IsNullOrEmpty(Password_RegisterView_TextBox)) { MessageBox.Show("Lütfen tüm alanları doldurun.", "Hata", MessageBoxButton.OK, MessageBoxImage.Error); + _logger.LogWarning("Kayıt işlemi için gerekli alanlar boş bırakıldı."); return; } @@ -49,6 +53,7 @@ private async Task Register_RegisterView_Button() if (!isValidEmail) { MessageBox.Show("Lütfen geçerli bir e-posta adresi girin.", "Hata", MessageBoxButton.OK, MessageBoxImage.Error); + _logger.LogWarning("Kayıt işlemi için geçersiz e-posta adresi girildi: {Email}", Email_RegisterView_TextBox); return; } @@ -59,9 +64,11 @@ private async Task Register_RegisterView_Button() if (!isInitiateRegistration) { MessageBox.Show("Kayıt işlemi başarısız oldu. Lütfen daha sonra tekrar deneyin.", "Hata", MessageBoxButton.OK, MessageBoxImage.Error); + _logger.LogError("Kayıt işlemi başarısız oldu. E-posta: {Email}", Email_RegisterView_TextBox); return; } MessageBox.Show("Kayıt işlemi başarılı! Lütfen e-posta adresinize gelen doğrulama kodunu girin.", "Bilgi", MessageBoxButton.OK, MessageBoxImage.Information); + _logger.LogInformation("Kayıt işlemi başarılı. E-posta: {Email}", Email_RegisterView_TextBox); // Store user information in the static manager NewUserInformationManager.FullName = FullName_RegisterView_TextBox; @@ -79,12 +86,14 @@ private void TogglePasswordVisibility_RegisterView_Button() EyeIconSource_RegisterView_Image = IsPasswordVisible_RegisterView_PasswordBoxAndTextBox ? "/Assets/Images/Icons/eyeopen.png" : "/Assets/Images/Icons/eyeclose.png"; + _logger.LogInformation("Şifre görünürlüğü değiştirildi. Yeni durum: {IsVisible}", IsPasswordVisible_RegisterView_PasswordBoxAndTextBox); } [RelayCommand] private void NavigateToLogin_RegisterView_Button() { NavigateToLoginRequested?.Invoke(); + _logger.LogInformation("Kullanıcı kayıt ekranından giriş ekranına yönlendirildi."); } } } \ No newline at end of file diff --git a/FinTrack/ViewModels/ReportsViewModel.cs b/FinTrack/ViewModels/ReportsViewModel.cs new file mode 100644 index 0000000..a58fa8c --- /dev/null +++ b/FinTrack/ViewModels/ReportsViewModel.cs @@ -0,0 +1,8 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace FinTrack.ViewModels +{ + public partial class ReportsViewModel : ObservableObject + { + } +} diff --git a/FinTrack/ViewModels/SettingsViewModel.cs b/FinTrack/ViewModels/SettingsViewModel.cs new file mode 100644 index 0000000..fac77a7 --- /dev/null +++ b/FinTrack/ViewModels/SettingsViewModel.cs @@ -0,0 +1,8 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace FinTrack.ViewModels +{ + public partial class SettingsViewModel : ObservableObject + { + } +} diff --git a/FinTrack/ViewModels/TopBarViewModel.cs b/FinTrack/ViewModels/TopBarViewModel.cs index 47ccd05..26793d1 100644 --- a/FinTrack/ViewModels/TopBarViewModel.cs +++ b/FinTrack/ViewModels/TopBarViewModel.cs @@ -1,6 +1,150 @@ -namespace FinTrack.ViewModels +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using FinTrack.Dtos; +using FinTrack.Messages; +using FinTrack.Services.Api; +using Microsoft.Extensions.Logging; + +namespace FinTrack.ViewModels { - internal class TopBarViewModel + public partial class TopBarViewModel : ObservableObject, IRecipient { + [ObservableProperty] + private string? _userAvatar_TopBarView_Image; + + [ObservableProperty] + private string? _userFullName_TopBarView_TextBlock; + + [ObservableProperty] + private string? _userEmail_TopBarView_TextBlock; + + [ObservableProperty] + private string? _userMembershipType_TopBarView_TextBlock; + + public event Action? NavigateToDashboardRequested; + public event Action? NavigateToAccountRequested; + public event Action? NavigateToBudegtRequested; + public event Action? NavigateToTransactionsRequested; + public event Action? NavigateToCurrenciesRequested; + public event Action? NavigateToDebtRequested; + public event Action? NavigateToReportsRequested; + public event Action? NavigateToFinBotRequested; + public event Action? NavigateToFeedbackRequested; + public event Action? NavigateToSettingsRequested; + public event Action? NavigateToNotificationRequested; + + private readonly ILogger _logger; + private readonly IApiService _apiService; + private readonly IMessenger _messenger; + + public TopBarViewModel(ILogger logger, IApiService apiService, IMessenger messenger) + { + _logger = logger; + _apiService = apiService; + _messenger = messenger; + + _messenger.Register(this); + } + + public void Receive(LoginSuccessMessage message) + { + _logger.LogInformation("Login başarılı, TopBarViewModel profil bilgilerini yüklüyor."); + _ = LoadProfile(); + } + + private async Task LoadProfile() + { + var userProfile = await _apiService.GetAsync("user"); + + if (userProfile != null) + { + UserAvatar_TopBarView_Image = userProfile.ProfilePicture; + UserFullName_TopBarView_TextBlock = userProfile.UserName; + UserEmail_TopBarView_TextBlock = userProfile.Email; + UserMembershipType_TopBarView_TextBlock = userProfile.MembershipType; + } + else + { + _logger.LogWarning("Kullanıcı profili yüklenemedi."); + } + } + + [RelayCommand] + private void NavigateToDashboard_TopBarView_Button() + { + NavigateToDashboardRequested?.Invoke(); + _logger.LogInformation("Kullanıcı Dashboard paneline geçti."); + } + + [RelayCommand] + private void NavigateToAccount_TopBarView_Button() + { + NavigateToAccountRequested?.Invoke(); + _logger.LogInformation("Kullanıcı Account paneline geçti."); + } + + [RelayCommand] + private void NavigateToBudget_TopBarView_Button() + { + NavigateToBudegtRequested?.Invoke(); + _logger.LogInformation("Kullanıcı Budget paneline geçti."); + } + + [RelayCommand] + private void NavigateToTransactions_TopBarView_Button() + { + NavigateToTransactionsRequested?.Invoke(); + _logger.LogInformation("Kullanıcı Transactions paneline geçti."); + } + + [RelayCommand] + private void NavigateToCurrencies_TopBarView_Button() + { + NavigateToCurrenciesRequested?.Invoke(); + _logger.LogInformation("Kullanıcı Currencies paneline geçti."); + } + + [RelayCommand] + private void NavigateToDebt_TopBarView_Button() + { + NavigateToDebtRequested?.Invoke(); + _logger.LogInformation("Kullanıcı Debt paneline geçti."); + } + + [RelayCommand] + private void NavigateToReports_TopBarView_Button() + { + NavigateToReportsRequested?.Invoke(); + _logger.LogInformation("Kullanıcı Reports paneline geçti."); + } + + [RelayCommand] + private void NavigateToFinBot_TopBarView_Button() + { + NavigateToFinBotRequested?.Invoke(); + _logger.LogInformation("Kullanıcı FinBot paneline geçti."); + } + + [RelayCommand] + private void NavigateToFeedback_TopBarView_Button() + { + NavigateToFeedbackRequested?.Invoke(); + _logger.LogInformation("Kullanıcı Feedback paneline geçti."); + } + + [RelayCommand] + private void NavigateToNotification_TopBarView_Button() + { + NavigateToNotificationRequested?.Invoke(); + _logger.LogInformation("Kullanıcı Notification paneline geçti."); + } + + [RelayCommand] + private void NavigateToSettings_TopBarView_Button() + { + NavigateToSettingsRequested?.Invoke(); + _logger.LogInformation("Kullanıcı Settings paneline geçti."); + } } } diff --git a/FinTrack/ViewModels/TransactionsViewModel.cs b/FinTrack/ViewModels/TransactionsViewModel.cs new file mode 100644 index 0000000..d7cecce --- /dev/null +++ b/FinTrack/ViewModels/TransactionsViewModel.cs @@ -0,0 +1,8 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace FinTrack.ViewModels +{ + public partial class TransactionsViewModel : ObservableObject + { + } +} diff --git a/FinTrack/Views/LoginWindow.xaml b/FinTrack/Views/AuthenticatorWindow.xaml similarity index 98% rename from FinTrack/Views/LoginWindow.xaml rename to FinTrack/Views/AuthenticatorWindow.xaml index 05f62bf..afb1e04 100644 --- a/FinTrack/Views/LoginWindow.xaml +++ b/FinTrack/Views/AuthenticatorWindow.xaml @@ -1,4 +1,4 @@ - + { + private readonly IHost _host; + + public AuthenticatorWindow(AuthenticatorViewModel viewModel, IHost host) + { + InitializeComponent(); + + this.DataContext = viewModel; + + _host = host; + + WeakReferenceMessenger.Default.Register(this); + + this.Unloaded += (s, e) => WeakReferenceMessenger.Default.UnregisterAll(this); + } + + public void Receive(LoginSuccessMessage message) + { + var mainWindow = _host.Services.GetRequiredService(); + mainWindow.Show(); + + Application.Current.Dispatcher.Invoke(this.Close); + } + + private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + if (e.ButtonState == MouseButtonState.Pressed) + { + this.DragMove(); + } + } + + private void CloseButton_Click(object sender, RoutedEventArgs e) + { + Application.Current.Shutdown(); + } + + private void MinimizeButton_Click(object sender, RoutedEventArgs e) + { + this.WindowState = WindowState.Minimized; + } + } +} \ No newline at end of file diff --git a/FinTrack/Views/DashboardView.xaml b/FinTrack/Views/DashboardView.xaml index 5c00042..9598713 100644 --- a/FinTrack/Views/DashboardView.xaml +++ b/FinTrack/Views/DashboardView.xaml @@ -7,195 +7,150 @@ mc:Ignorable="d" d:DataContext="{d:DesignInstance Type=viewmodels:DashboardViewModel, IsDesignTimeCreatable=True}" Background="{StaticResource MainBackgroundBrush}" - Height="900" Width="1920"> - - - - - - - - - - - - - - - + Padding="15"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + - + \ No newline at end of file diff --git a/FinTrack/Views/LoginWindow.xaml.cs b/FinTrack/Views/LoginWindow.xaml.cs deleted file mode 100644 index 02d7e3d..0000000 --- a/FinTrack/Views/LoginWindow.xaml.cs +++ /dev/null @@ -1,33 +0,0 @@ -using FinTrack.ViewModels; -using System.Windows; -using System.Windows.Input; - -namespace FinTrack -{ - public partial class LoginWindow : Window - { - public LoginWindow() - { - InitializeComponent(); - this.DataContext = new MainViewModel(); - } - - private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) - { - if (e.ButtonState == MouseButtonState.Pressed) - { - this.DragMove(); - } - } - - private void CloseButton_Click(object sender, RoutedEventArgs e) - { - Application.Current.Shutdown(); - } - - private void MinimizeButton_Click(object sender, RoutedEventArgs e) - { - this.WindowState = WindowState.Minimized; - } - } -} \ No newline at end of file diff --git a/FinTrack/Views/MainWindow.xaml b/FinTrack/Views/MainWindow.xaml index fa325c7..382a61a 100644 --- a/FinTrack/Views/MainWindow.xaml +++ b/FinTrack/Views/MainWindow.xaml @@ -6,31 +6,79 @@ xmlns:local="clr-namespace:FinTrack" xmlns:views="clr-namespace:FinTrack.Views" xmlns:viewmodels="clr-namespace:FinTrack.ViewModels" - mc:Ignorable="d" - Title="FinTrack" Height="1080" Width="1920" + mc:Ignorable="d" Title="FinTrack" WindowStartupLocation="CenterScreen" MinWidth="1280" MinHeight="720" + WindowState="Maximized" Background="{StaticResource MainBackgroundBrush}"> - - - + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + - + - + \ No newline at end of file diff --git a/FinTrack/Views/MainWindow.xaml.cs b/FinTrack/Views/MainWindow.xaml.cs index 4f34ba2..1190562 100644 --- a/FinTrack/Views/MainWindow.xaml.cs +++ b/FinTrack/Views/MainWindow.xaml.cs @@ -1,12 +1,17 @@ -using System.Windows; +using FinTrack.ViewModels; +using System.Windows; namespace FinTrack.Views { public partial class MainWindow : Window { - public MainWindow() + private readonly MainViewModel _mainViewModel; + + public MainWindow(MainViewModel mainViewModel) { InitializeComponent(); + _mainViewModel = mainViewModel; + DataContext = _mainViewModel; } } } diff --git a/FinTrack/Views/TopBarView.xaml b/FinTrack/Views/TopBarView.xaml index 6d8ed6e..1a4200f 100644 --- a/FinTrack/Views/TopBarView.xaml +++ b/FinTrack/Views/TopBarView.xaml @@ -5,7 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:viewmodels="clr-namespace:FinTrack.ViewModels" mc:Ignorable="d" - d:DataContext="{d:DesignInstance Type=viewmodels:MainViewModel, IsDesignTimeCreatable=True}" + d:DataContext="{d:DesignInstance Type=viewmodels:TopBarViewModel, IsDesignTimeCreatable=True}" d:DesignWidth="1920"> @@ -30,18 +30,18 @@ - + - - - @@ -49,26 +49,50 @@ - - - - - - - - - - + + + + + + + + + + + + + + +