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/Controls/CritterEventButton.axaml.cs b/critterfolio/CritterFolio/CritterFolio/Controls/CritterEventButton.axaml.cs
new file mode 100644
index 0000000..e5dac9b
--- /dev/null
+++ b/critterfolio/CritterFolio/CritterFolio/Controls/CritterEventButton.axaml.cs
@@ -0,0 +1,53 @@
+using System;
+using System.IO;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml;
+using CritterFolio.DataModels;
+using CritterFolio.Pages;
+using CritterFolio.Services;
+
+namespace CritterFolio.Controls;
+
+public partial class CritterEventButton : UserControl
+{
+ private CritterEvent? _critterEvent;
+ public event EventHandler? Deleted;
+
+ public CritterEventButton()
+ {
+ InitializeComponent();
+ }
+
+ public void Init(CritterEvent critterEvent)
+ {
+ _critterEvent = critterEvent;
+
+ NameLabel.Content = critterEvent.Name;
+
+ ViewButton.Click += ViewButtonOnClick;
+ DeleteButton.Click += DeleteButtonOnClick;
+ }
+
+ private async void DeleteButtonOnClick(object? sender, RoutedEventArgs e)
+ {
+ if (_critterEvent is null) return;
+
+ var result = await DialogHelper.ShowConfirmationDialog("Are you sure you want to delete the event?");
+ if (!result) return;
+
+ await DatabaseService.DeleteEvent(_critterEvent);
+
+ Deleted?.Invoke(this, EventArgs.Empty);
+ }
+
+ private async void ViewButtonOnClick(object? sender, RoutedEventArgs e)
+ {
+ if (_critterEvent is null) return;
+
+ var page = new EventPage();
+ page.Init(_critterEvent);
+ Sys.Navigation?.PushPage(page);
+ }
+}
\ No newline at end of file
diff --git a/critterfolio/CritterFolio/CritterFolio/Controls/DocumentItem.axaml b/critterfolio/CritterFolio/CritterFolio/Controls/DocumentItem.axaml
index 6700657..4095cc1 100644
--- a/critterfolio/CritterFolio/CritterFolio/Controls/DocumentItem.axaml
+++ b/critterfolio/CritterFolio/CritterFolio/Controls/DocumentItem.axaml
@@ -2,7 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- mc:Ignorable="d" d:DesignWidth="720" d:DesignHeight="1280"
+ mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="500"
x:Class="CritterFolio.Controls.DocumentItem">
+ FontSize="20" />
diff --git a/critterfolio/CritterFolio/CritterFolio/DataModels/CritterEvent.cs b/critterfolio/CritterFolio/CritterFolio/DataModels/CritterEvent.cs
new file mode 100644
index 0000000..c0091b8
--- /dev/null
+++ b/critterfolio/CritterFolio/CritterFolio/DataModels/CritterEvent.cs
@@ -0,0 +1,15 @@
+using System;
+using SQLite;
+
+namespace CritterFolio.DataModels;
+
+[Table("Events")]
+public class CritterEvent
+{
+ [PrimaryKey, AutoIncrement]
+ public int Id { get; set; }
+ public int CritterId { get; set; }
+ public string Name { get; set; } = "New Event";
+ public DateTime DateTime { get; set; } = DateTime.Now;
+ public string? Description { get; set; } = "New Event";
+}
\ No newline at end of file
diff --git a/critterfolio/CritterFolio/CritterFolio/Pages/CritterPage.axaml b/critterfolio/CritterFolio/CritterFolio/Pages/CritterPage.axaml
index 43af798..54706b4 100644
--- a/critterfolio/CritterFolio/CritterFolio/Pages/CritterPage.axaml
+++ b/critterfolio/CritterFolio/CritterFolio/Pages/CritterPage.axaml
@@ -39,12 +39,19 @@
+
+
+
+
+
+
+
-
+
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