FrymasterBadgeApp/CompanyPage.xaml.cs

303 lines
9.9 KiB
C#

using System.Diagnostics;
using FrymasterBadgeApp.Services;
using Microsoft.Data.SqlClient;
using Microsoft.Maui.Storage;
namespace FrymasterBadgeApp;
public partial class CompanyPage : ContentPage
{
private readonly SqlService _db;
private Dictionary<string, object>? _selectedCompany;
private string _tempLogoPath = "";
private const string LogoPathKey = "LogoBasePath";
/// <summary>
/// Constructor for CompanyPage.
/// of type ContentPage.
/// the main page for viewing and editing company information.
/// <param name="db">The SQLService object used to access the database.</param>
public CompanyPage(SqlService db)
{
InitializeComponent();
_db = db;
Shell.SetBackButtonBehavior(
this,
new BackButtonBehavior
{
TextOverride = "Back",
Command = new Command(async () => await Shell.Current.GoToAsync("..")),
}
);
LoadCompanies();
}
/// <summary>
/// Returns the path to the directory used to store company logos.
/// </summary>
/// <returns>The path to the directory used to store company logos.</returns>
private string GetLogoDirectory() =>
Preferences.Default.Get(LogoPathKey, @"C:\FrymasterData\logos");
/// <summary>
/// Loads the list of companies from the database.
/// the results are ordered by company name (ascending).
/// and displays the companies are stored in the CompanyList.
/// </summary>
private void LoadCompanies()
{
try
{
var companies = _db.Query("SELECT * FROM Companies ORDER BY Name ASC", null);
CompanyList.ItemsSource = companies;
}
catch (Exception ex)
{
AppLogger.Error("Failed to load companies", ex);
DisplayAlert("Error", "Failed to load companies.", "OK");
}
}
/// <summary>
/// Called when the selection in the companies list changes.
/// the selection is used to update the company name, address, city, state, zip code, and delete button visibility.
/// </summary>
private void OnCompanySelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selected = e.CurrentSelection.FirstOrDefault() as Dictionary<string, object>;
if (selected == null)
{
ClearFields();
return;
}
_selectedCompany = selected;
MainThread.BeginInvokeOnMainThread(async () =>
{
CompName.Text = GetValue(selected, "Name");
CompAddress.Text = GetValue(selected, "Address");
CompCity.Text = GetValue(selected, "City");
CompState.Text = GetValue(selected, "State");
CompZip.Text = GetValue(selected, "Zip");
DeleteBtn.IsVisible = true;
LogoPreview.Source = null;
_tempLogoPath = "";
// Get stored filename only
string storedFileName = GetValue(selected, "logo");
if (!string.IsNullOrWhiteSpace(storedFileName))
{
string logoDir = GetLogoDirectory();
string fullPath = Path.Combine(logoDir, storedFileName);
if (File.Exists(fullPath))
{
_tempLogoPath = fullPath;
LogoPreview.Source = ImageSource.FromFile(fullPath);
AppLogger.Info($"Logo loaded for company: {fullPath}");
}
else
{
AppLogger.Warning($"Logo file missing: {fullPath}");
}
}
});
}
/// <summary>
/// Returns the value of the given key from the dictionary, trimmed of whitespace.
/// If the key does not exist, an empty string is returned.
/// </summary>
/// <param name="dict">The dictionary to search.</param>
/// <param name="key">The key to search for.</param>
/// <returns>The value of the given key, trimmed of whitespace, or an empty string if the key does not exist.</returns>
private string GetValue(Dictionary<string, object> dict, string key)
{
var actualKey = dict.Keys.FirstOrDefault(k =>
k.Equals(key, StringComparison.OrdinalIgnoreCase)
);
return actualKey != null ? dict[actualKey]?.ToString()?.Trim() ?? "" : "";
}
/// <summary>
/// Called when the user selects a logo image from the file picker.
/// </summary>
/// <param name="sender">The object that triggered the event</param>
/// <param name="e">The event data which contains information about the event</param>
private async void OnSelectLogoClicked(object sender, EventArgs e)
{
try
{
var result = await FilePicker.Default.PickAsync(
new PickOptions
{
PickerTitle = "Select Company Logo",
FileTypes = FilePickerFileType.Images,
}
);
if (result != null)
{
_tempLogoPath = result.FullPath;
LogoPreview.Source = ImageSource.FromFile(_tempLogoPath);
}
}
catch (Exception ex)
{
await DisplayAlert("Error", $"Failed to select logo: {ex.Message}", "OK");
}
}
/// <summary>
/// Save the selected company to the database.
/// </summary>
/// <param name="sender">The object that invoked this method.</param>
/// <param name="e">The event data for this method.</param>
private async void OnSaveClicked(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(CompName.Text))
{
await DisplayAlert("Error", "Company Name is required", "OK");
return;
}
string logoDir = GetLogoDirectory();
if (!Directory.Exists(logoDir))
{
try
{
Directory.CreateDirectory(logoDir);
}
catch
{
await DisplayAlert("Error", "Cannot create logo directory.", "OK");
return;
}
}
string logoFileNameToSave = ""; // will be stored in DB
if (!string.IsNullOrEmpty(_tempLogoPath))
{
try
{
string originalFileName = Path.GetFileName(_tempLogoPath);
string extension = Path.GetExtension(originalFileName);
string baseName = Path.GetFileNameWithoutExtension(originalFileName);
// Avoid overwrites: add timestamp if file already exists
string destFileName = originalFileName;
string destPath = Path.Combine(logoDir, destFileName);
if (File.Exists(destPath))
{
destFileName = $"{baseName}_{DateTime.Now:yyyyMMdd_HHmmss}{extension}";
destPath = Path.Combine(logoDir, destFileName);
}
File.Copy(_tempLogoPath, destPath, true);
logoFileNameToSave = destFileName; // only filename saved to DB
_tempLogoPath = destPath; // update local temp for preview
AppLogger.Info($"Logo saved as: {destPath}");
}
catch (Exception ex)
{
AppLogger.Error("Failed to copy logo", ex);
await DisplayAlert("Error", "Failed to save logo file.", "OK");
return;
}
}
try
{
var parameters = new List<SqlParameter>
{
new("@n", CompName.Text ?? ""),
new("@a", CompAddress.Text ?? ""),
new("@c", CompCity.Text ?? ""),
new("@s", CompState.Text ?? ""),
new("@z", CompZip.Text ?? ""),
new("@l", logoFileNameToSave), // ← only filename
};
if (_selectedCompany == null)
{
_db.Execute(
"INSERT INTO Companies (Name, Address, City, State, Zip, logo) VALUES (@n, @a, @c, @s, @z, @l)",
parameters.ToArray()
);
await DisplayAlert("Success", "Company added.", "OK");
}
else
{
parameters.Add(new("@id", _selectedCompany["ID"]));
_db.Execute(
"UPDATE Companies SET Name=@n, Address=@a, City=@c, State=@s, Zip=@z, logo=@l WHERE ID=@id",
parameters.ToArray()
);
await DisplayAlert("Success", "Company updated.", "OK");
}
LoadCompanies();
ClearFields();
}
catch (Exception ex)
{
AppLogger.Error("Database save failed", ex);
await DisplayAlert("Database Error", ex.Message, "OK");
}
}
/// <summary>
/// Resets the UI state after clicking the "Add New" button.
/// </summary>
private void OnAddNewClicked(object sender, EventArgs e)
{
CompanyList.SelectedItem = null;
ClearFields();
}
/// <summary>
/// Resets the UI state after clicking the "Add New" button or deleting a company.
/// </summary>
private void ClearFields()
{
_selectedCompany = null;
_tempLogoPath = "";
CompName.Text = CompAddress.Text = CompCity.Text = CompState.Text = CompZip.Text = "";
LogoPreview.Source = null;
DeleteBtn.IsVisible = false;
}
/// <summary>
/// Delete a company from the database.
/// </summary>
private async void OnDeleteClicked(object sender, EventArgs e)
{
if (_selectedCompany == null)
return;
if (await DisplayAlert("Confirm", $"Delete {CompName.Text}?", "Yes", "No"))
{
try
{
_db.Execute(
"DELETE FROM Companies WHERE ID=@id",
new SqlParameter[] { new("@id", _selectedCompany["ID"]) }
);
LoadCompanies();
ClearFields();
}
catch (Exception ex)
{
await DisplayAlert("Error", ex.Message, "OK");
}
}
}
}