Can now schedule events
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
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="800" d:DesignHeight="450"
|
||||
x:Class="CritterFolio.Controls.CritterEventButton">
|
||||
<Grid HorizontalAlignment="Stretch" Height="50"
|
||||
Background="{DynamicResource AccentBackground}"
|
||||
x:Name="MainGrid"
|
||||
ColumnDefinitions="Auto, *, Auto">
|
||||
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||
<Label FontFamily="{DynamicResource Phosphor}"
|
||||
Foreground="{DynamicResource PrimaryForeground}"
|
||||
VerticalAlignment="Center"
|
||||
Content=""
|
||||
FontSize="25"/>
|
||||
<Label x:Name="NameLabel"
|
||||
Margin="10, 0"
|
||||
Content="Event Name/Date"
|
||||
Foreground="{DynamicResource PrimaryForeground}"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="20" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal">
|
||||
<Button x:Name="ViewButton" Classes="icon" Content="" />
|
||||
<Button x:Name="DeleteButton" Classes="icon" Content="" Foreground="Red" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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">
|
||||
<Grid HorizontalAlignment="Stretch" Height="50"
|
||||
Background="{DynamicResource AccentBackground}"
|
||||
@@ -20,9 +20,7 @@
|
||||
Content="Document"
|
||||
Foreground="{DynamicResource PrimaryForeground}"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="20"
|
||||
MaxWidth="150"
|
||||
/>
|
||||
FontSize="20" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal">
|
||||
<Button x:Name="ViewButton" Classes="icon" Content="" />
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
@@ -39,12 +39,19 @@
|
||||
<TextBox x:Name="NotesBox" MinHeight="100" AcceptsReturn="True" TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
|
||||
<Grid ColumnDefinitions="Auto, *, Auto" Margin="0, 10">
|
||||
<Label Grid.Column="0" Classes="heading1">Events</Label>
|
||||
<Button x:Name="AddEventButton" Grid.Column="2">Add New</Button>
|
||||
</Grid>
|
||||
|
||||
<StackPanel x:Name="EventsStack" Spacing="5" HorizontalAlignment="Stretch" Margin="20, 5"/>
|
||||
|
||||
<Grid ColumnDefinitions="Auto, *, Auto" Margin="0, 10">
|
||||
<Label Grid.Column="0" Classes="heading1">Documents</Label>
|
||||
<Button x:Name="AddDocButton" Grid.Column="2">Add New</Button>
|
||||
</Grid>
|
||||
|
||||
<StackPanel x:Name="DocsStack" HorizontalAlignment="Stretch" Margin="20, 5"/>
|
||||
<StackPanel x:Name="DocsStack" Spacing="5" HorizontalAlignment="Stretch" Margin="20, 5"/>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
@@ -20,6 +20,7 @@ public partial class CritterPage : Page
|
||||
private Critter? _critter;
|
||||
private List<CritterComboBoxDisplay> _critterDisplays = [];
|
||||
private List<Document> _documents = [];
|
||||
private List<CritterEvent> _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);
|
||||
}
|
||||
|
||||
}
|
||||
17
critterfolio/CritterFolio/CritterFolio/Pages/EventPage.axaml
Normal file
17
critterfolio/CritterFolio/CritterFolio/Pages/EventPage.axaml
Normal file
@@ -0,0 +1,17 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
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="800" d:DesignHeight="450"
|
||||
x:Class="CritterFolio.Pages.EventPage">
|
||||
<StackPanel>
|
||||
<Label>Name:</Label>
|
||||
<TextBox x:Name="NameBox" Watermark="Name" />
|
||||
<Label>Date:</Label>
|
||||
<CalendarDatePicker x:Name="DatePicker" />
|
||||
<Label>Time:</Label>
|
||||
<TimePicker x:Name="TimePicker" />
|
||||
<Label>Description:</Label>
|
||||
<TextBox x:Name="DescriptionBox" Height="500" />
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
112
critterfolio/CritterFolio/CritterFolio/Pages/EventPage.axaml.cs
Normal file
112
critterfolio/CritterFolio/CritterFolio/Pages/EventPage.axaml.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ public static class DatabaseService
|
||||
|
||||
try
|
||||
{
|
||||
await _db.CreateTablesAsync<Critter, Document>();
|
||||
await _db.CreateTablesAsync<Critter, Document, CritterEvent>();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -37,6 +37,7 @@ public static class DatabaseService
|
||||
await Init();
|
||||
await _db?.DeleteAllAsync<Critter>()!;
|
||||
await _db?.DeleteAllAsync<Document>()!;
|
||||
await _db?.DeleteAllAsync<CritterEvent>()!;
|
||||
}
|
||||
|
||||
#region Critter operations
|
||||
@@ -137,4 +138,49 @@ public static class DatabaseService
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CritterEvent operations
|
||||
|
||||
public static async Task<bool> AddEvent(CritterEvent critterEvent)
|
||||
{
|
||||
await Init();
|
||||
var result = await _db?.InsertAsync(critterEvent)!;
|
||||
return !(result <= 0);
|
||||
}
|
||||
|
||||
public static async Task<bool> UpdateEvent(CritterEvent critterEvent)
|
||||
{
|
||||
await Init();
|
||||
return await _db?.UpdateAsync(critterEvent)! != 0;
|
||||
}
|
||||
|
||||
public static async Task<bool> DeleteEvent(CritterEvent critterEvent)
|
||||
{
|
||||
await Init();
|
||||
return await _db?.DeleteAsync(critterEvent)! != 0;
|
||||
}
|
||||
|
||||
public static async Task<bool> DeleteEvent(int id)
|
||||
{
|
||||
await Init();
|
||||
return await _db?.DeleteAsync<CritterEvent>(id)! != 0;
|
||||
}
|
||||
|
||||
public static async Task<List<CritterEvent>> GetAllEvents()
|
||||
{
|
||||
await Init();
|
||||
var result = await _db?.Table<CritterEvent>().ToListAsync()!;
|
||||
return result ?? [];
|
||||
}
|
||||
|
||||
public static async Task<List<CritterEvent>> GetAllEventsForCritter(int critterId)
|
||||
{
|
||||
await Init();
|
||||
var result = await _db?.Table<CritterEvent>()
|
||||
.Where(ce => ce.CritterId == critterId)
|
||||
.ToListAsync()!;
|
||||
return result ?? [];
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user