diff --git a/critterfolio/CritterFolio/CritterFolio/Controls/CritterEventButton.axaml b/critterfolio/CritterFolio/CritterFolio/Controls/CritterEventButton.axaml new file mode 100644 index 0000000..23f0c1e --- /dev/null +++ b/critterfolio/CritterFolio/CritterFolio/Controls/CritterEventButton.axaml @@ -0,0 +1,30 @@ + + + + + + + + + + + - + diff --git a/critterfolio/CritterFolio/CritterFolio/Pages/CritterPage.axaml.cs b/critterfolio/CritterFolio/CritterFolio/Pages/CritterPage.axaml.cs index 9131be3..622b5e9 100644 --- a/critterfolio/CritterFolio/CritterFolio/Pages/CritterPage.axaml.cs +++ b/critterfolio/CritterFolio/CritterFolio/Pages/CritterPage.axaml.cs @@ -20,6 +20,7 @@ public partial class CritterPage : Page private Critter? _critter; private List _critterDisplays = []; private List _documents = []; + private List _critterEvents = []; public CritterPage() { @@ -30,20 +31,36 @@ public partial class CritterPage : Page { _critter = critter; - PfpButton.Click += OpenFileButton_Clicked; GenderOption.ItemsSource = Enum.GetNames(typeof(Gender)); - AddDocButton.Click += AddDocButtonOnClick; - await UpdateInfo(); } - public override void Refresh() { base.Refresh(); + + Sys.Navigation?.SetTitle(_critter?.Name ?? "Error"); + CreateHeaderButtons(); + AddConnections(); + RegenEventsList(); + RegenDocsList(); + } + + private void AddConnections() + { + PfpButton.Click += OpenFileButton_Clicked; + AddDocButton.Click += AddDocButtonOnClick; + AddEventButton.Click += AddEventButtonOnClick; + } + + protected override void ClearConnections() + { + PfpButton.Click -= OpenFileButton_Clicked; + AddDocButton.Click -= AddDocButtonOnClick; + AddEventButton.Click -= AddEventButtonOnClick; } private async Task LoadComboItemsAsync() @@ -133,10 +150,40 @@ public partial class CritterPage : Page } RegenDocsList(); + RegenEventsList(); + } + + private async void RegenEventsList() + { + if (_critter is null) return; + + _critterEvents.Clear(); + var events = await DatabaseService.GetAllEventsForCritter(_critter.Id); + _critterEvents.AddRange(events); + + EventsStack.Children.Clear(); + foreach (var critterEvent in _critterEvents) + { + CreateCritterEventItem(critterEvent); + } + } + + private void CreateCritterEventItem(CritterEvent critterEvent) + { + var item = new CritterEventButton(); + item.Init(critterEvent); + EventsStack.Children.Add(item); + + item.Deleted += async (sender, args) => + { + RegenEventsList(); + }; } private async void RegenDocsList() { + if (_critter is null) return; + _documents.Clear(); var docs = await DatabaseService.GetAllDocumentsForCritter(_critter.Id); _documents.AddRange(docs); @@ -375,5 +422,19 @@ public partial class CritterPage : Page CreateDocItem(doc); } + + private async void AddEventButtonOnClick(object? sender, RoutedEventArgs e) + { + if (_critter is null) return; + + var newEvent = new CritterEvent() + { + CritterId = _critter.Id + }; + await DatabaseService.AddEvent(newEvent); + var page = new EventPage(); + page.Init(newEvent); + Sys.Navigation?.PushPage(page); + } } \ No newline at end of file diff --git a/critterfolio/CritterFolio/CritterFolio/Pages/EventPage.axaml b/critterfolio/CritterFolio/CritterFolio/Pages/EventPage.axaml new file mode 100644 index 0000000..7ecae21 --- /dev/null +++ b/critterfolio/CritterFolio/CritterFolio/Pages/EventPage.axaml @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/critterfolio/CritterFolio/CritterFolio/Pages/EventPage.axaml.cs b/critterfolio/CritterFolio/CritterFolio/Pages/EventPage.axaml.cs new file mode 100644 index 0000000..5dbccf4 --- /dev/null +++ b/critterfolio/CritterFolio/CritterFolio/Pages/EventPage.axaml.cs @@ -0,0 +1,112 @@ +using System; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Markup.Xaml; +using Avalonia.Media; +using CritterFolio.DataModels; +using CritterFolio.Services; + +namespace CritterFolio.Pages; + +public partial class EventPage : Page +{ + private CritterEvent? _critterEvent; + + public EventPage() + { + InitializeComponent(); + } + + public void Init(CritterEvent critterEvent) + { + _critterEvent = critterEvent; + } + + public override void Refresh() + { + base.Refresh(); + + UpdateInfo(); + + var saveBttn = new Button() + { + Classes = { "headerBttn" }, + Content = "\ue248" + }; + + saveBttn.Click += SaveButtonClicked; + Sys.Navigation?.AddHeaderButton(saveBttn); + + var delBttn = new Button() + { + Classes = { "headerBttn" }, + Content = "\ue4a6" + }; + delBttn.Foreground = new SolidColorBrush(Colors.Red); + delBttn.Click += DeleteButtonClicked; + Sys.Navigation?.AddHeaderButton(delBttn); + } + + private void UpdateInfo() + { + Sys.Navigation?.SetTitle(_critterEvent?.Name ?? "New Event"); + + NameBox.Text = _critterEvent?.Name; + DatePicker.SelectedDate = _critterEvent?.DateTime.Date; + TimePicker.SelectedTime = _critterEvent?.DateTime.ToLocalTime().TimeOfDay; + DescriptionBox.Text = _critterEvent?.Description; + } + + private async void DeleteButtonClicked(object? sender, RoutedEventArgs e) + { + if (_critterEvent is null) return; + + var result = await DialogHelper.ShowConfirmationDialog("Are you sure you wantt to delete this event?"); + if (!result) return; + + await DatabaseService.DeleteEvent(_critterEvent); + + Sys.Navigation?.PopPage(); + } + + private async void SaveButtonClicked(object? sender, RoutedEventArgs e) + { + if (_critterEvent is null) return; + + var result = await DialogHelper.ShowConfirmationDialog("Save this event?"); + if (!result) return; + + var validationResult = await ValidateAndSave(); + if (!validationResult.isValid) + { + await DialogHelper.ShowMessage($"Could not save event:\n{validationResult.errString}"); + return; + } + + await DatabaseService.UpdateEvent(_critterEvent); + } + + private async Task<(bool isValid, string errString)> ValidateAndSave() + { + if (_critterEvent is null) return (false, "Critter was null"); + + var err = ""; + + if (string.IsNullOrEmpty(NameBox.Text)) + { + err += "Name is required\n"; + } + else + { + _critterEvent.Name = NameBox.Text; + } + + _critterEvent.DateTime = new DateTime(DateOnly.FromDateTime(DatePicker.DisplayDate), TimeOnly.FromTimeSpan((TimeSpan)TimePicker.SelectedTime!)); + + _critterEvent.Description = DescriptionBox.Text; + + return string.IsNullOrEmpty(err) ? (true, err) : (false, err); + } +} \ No newline at end of file diff --git a/critterfolio/CritterFolio/CritterFolio/Services/DatabaseService.cs b/critterfolio/CritterFolio/CritterFolio/Services/DatabaseService.cs index 814a640..07b8c01 100644 --- a/critterfolio/CritterFolio/CritterFolio/Services/DatabaseService.cs +++ b/critterfolio/CritterFolio/CritterFolio/Services/DatabaseService.cs @@ -24,7 +24,7 @@ public static class DatabaseService try { - await _db.CreateTablesAsync(); + await _db.CreateTablesAsync(); } catch (Exception e) { @@ -37,6 +37,7 @@ public static class DatabaseService await Init(); await _db?.DeleteAllAsync()!; await _db?.DeleteAllAsync()!; + await _db?.DeleteAllAsync()!; } #region Critter operations @@ -137,4 +138,49 @@ public static class DatabaseService } #endregion + + #region CritterEvent operations + + public static async Task AddEvent(CritterEvent critterEvent) + { + await Init(); + var result = await _db?.InsertAsync(critterEvent)!; + return !(result <= 0); + } + + public static async Task UpdateEvent(CritterEvent critterEvent) + { + await Init(); + return await _db?.UpdateAsync(critterEvent)! != 0; + } + + public static async Task DeleteEvent(CritterEvent critterEvent) + { + await Init(); + return await _db?.DeleteAsync(critterEvent)! != 0; + } + + public static async Task DeleteEvent(int id) + { + await Init(); + return await _db?.DeleteAsync(id)! != 0; + } + + public static async Task> GetAllEvents() + { + await Init(); + var result = await _db?.Table().ToListAsync()!; + return result ?? []; + } + + public static async Task> GetAllEventsForCritter(int critterId) + { + await Init(); + var result = await _db?.Table() + .Where(ce => ce.CritterId == critterId) + .ToListAsync()!; + return result ?? []; + } + + #endregion } \ No newline at end of file diff --git a/critterfolio/CritterFolio/CritterFolioTests/UnitTest1.cs b/critterfolio/CritterFolio/CritterFolioTests/UnitTest1.cs index e486fd9..f87af3e 100644 --- a/critterfolio/CritterFolio/CritterFolioTests/UnitTest1.cs +++ b/critterfolio/CritterFolio/CritterFolioTests/UnitTest1.cs @@ -16,19 +16,12 @@ public class Tests [Test] public async Task TestAddingCritter() { - var homePage = new HomePage(); - var currentCritterDbCount = (await DatabaseService.GetAllCritters()).Count; - - homePage.AddButtonClicked(null, new RoutedEventArgs()); - - var newCritterDbCount = (await DatabaseService.GetAllCritters()).Count; - - Assert.That(newCritterDbCount == currentCritterDbCount + 1, Is.True); + Assert.That(await DatabaseService.AddCritter(new Critter(){Name = "Test Critter"}), Is.True); } [Test] public async Task TestGettingCritter() { - Assert.That(await DatabaseService.GetCritter(-1), Is.True); + Assert.That(await DatabaseService.GetCritter(-1), Is.True); } } \ No newline at end of file