Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CarryIQ.App/AppHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public static IHost BuildHost()
services.AddSingleton<ClubManagerViewModel>();
services.AddSingleton<SessionManagerViewModel>();
services.AddSingleton<ShotEntryViewModel>();
services.AddSingleton<ShotReviewViewModel>();
services.AddSingleton<MainWindowViewModel>();
services.AddSingleton<MainWindow>();
})
Expand Down
175 changes: 175 additions & 0 deletions src/CarryIQ.App/Controls/ShotReviewView.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
<UserControl x:Class="CarryIQ.App.ShotReviewView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CarryIQ.App"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<UserControl.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Margin" Value="0,4,0,0" />
<Setter Property="Padding" Value="12,8" />
<Setter Property="BorderBrush" Value="#C9D6E3" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Background" Value="White" />
</Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="Margin" Value="0,4,0,0" />
<Setter Property="Padding" Value="10,6" />
<Setter Property="BorderBrush" Value="#C9D6E3" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Background" Value="White" />
</Style>
</UserControl.Resources>

<Border Background="#FFFFFF"
BorderBrush="#D9E2EC"
BorderThickness="1"
CornerRadius="18"
Padding="24">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Margin="0,0,0,16">
<TextBlock Text="Shot review"
FontSize="24"
FontWeight="SemiBold"
Foreground="#102A43" />
<TextBlock Text="Use the grid to review, select, and bulk-correct recent shots without leaving the keyboard."
Margin="0,6,0,0"
Foreground="#627D98"
TextWrapping="Wrap" />

<Border Margin="0,16,0,0"
Padding="14"
Background="#F8FAFC"
BorderBrush="#E2E8F0"
BorderThickness="1"
CornerRadius="12">
<TextBlock Text="{Binding StatusMessage}"
Foreground="#334E68"
TextWrapping="Wrap" />
</Border>

<UniformGrid Columns="3" Margin="0,16,0,0">
<StackPanel Margin="0,0,12,12">
<TextBlock Text="Session" Foreground="#334E68" />
<ComboBox ItemsSource="{Binding Sessions}"
SelectedValue="{Binding SelectedSessionId, Mode=TwoWay}"
SelectedValuePath="Id">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Foreground="#102A43">
<Run Text="{Binding SessionDate, StringFormat={}{0:yyyy-MM-dd}}" />
<Run Text=" " />
<Run Text="{Binding Name}" />
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>

<StackPanel Margin="0,0,12,12">
<TextBlock Text="Search" Foreground="#334E68" />
<TextBox Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>

<StackPanel Margin="0,0,0,12">
<TextBlock Text="Filters" Foreground="#334E68" />
<CheckBox Content="Included only" IsChecked="{Binding IncludedOnly}" Margin="0,8,0,0" />
</StackPanel>
</UniformGrid>

<StackPanel Orientation="Horizontal" Margin="0,4,0,0">
<Button Content="Refresh" Command="{Binding RefreshCommand}" Padding="14,8" Margin="0,0,8,0" />
<Button Content="Include selected" Command="{Binding IncludeSelectedCommand}" Padding="14,8" Margin="0,0,8,0" />
<Button Content="Exclude selected" Command="{Binding ExcludeSelectedCommand}" Padding="14,8" Margin="0,0,8,0" />
<Button Content="Apply club" Command="{Binding ApplyClubCommand}" Padding="14,8" Margin="0,0,8,0" />
<Button Content="Apply swing type" Command="{Binding ApplySwingTypeCommand}" Padding="14,8" />
</StackPanel>
</StackPanel>

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2.2*" />
<RowDefinition Height="1.3*" />
</Grid.RowDefinitions>

<DataGrid Grid.Row="0"
ItemsSource="{Binding Shots}"
SelectedItem="{Binding SelectedShot, Mode=TwoWay}"
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserDeleteRows="False"
HeadersVisibility="Column"
SelectionMode="Single"
SelectionUnit="FullRow"
Background="White"
BorderBrush="#D9E2EC"
BorderThickness="1"
GridLinesVisibility="Horizontal"
RowBackground="White"
AlternatingRowBackground="#F8FAFC">
<DataGrid.Columns>
<DataGridTemplateColumn Width="48" Header="">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" HorizontalAlignment="Center" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="#" Binding="{Binding ShotSequence}" Width="60" IsReadOnly="True" />
<DataGridTextColumn Header="Recorded" Binding="{Binding RecordedAt, StringFormat={}{0:yyyy-MM-dd HH:mm}}" Width="160" IsReadOnly="True" />
<DataGridTextColumn Header="Club" Binding="{Binding ClubName}" Width="*" IsReadOnly="True" />
<DataGridTextColumn Header="Carry" Binding="{Binding DisplayCarryDistance}" Width="90" IsReadOnly="True" />
<DataGridTextColumn Header="State" Binding="{Binding InclusionState}" Width="100" IsReadOnly="True" />
<DataGridTextColumn Header="Swing" Binding="{Binding SwingType}" Width="120" IsReadOnly="True" />
<DataGridTextColumn Header="Notes" Binding="{Binding Notes}" Width="2*" IsReadOnly="True" />
</DataGrid.Columns>
</DataGrid>

<Border Grid.Row="1"
Margin="0,16,0,0"
Padding="18"
Background="#F8FAFC"
BorderBrush="#E2E8F0"
BorderThickness="1"
CornerRadius="14">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1.2*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>

<StackPanel Margin="0,0,18,0">
<TextBlock Text="Selected shot" FontWeight="SemiBold" Foreground="#102A43" />
<TextBlock Margin="0,8,0,0" Text="{Binding SelectedShot.Notes}" Foreground="#334E68" TextWrapping="Wrap" />
<TextBlock Margin="0,6,0,0" Text="{Binding SelectedShot.RawImportData}" Foreground="#627D98" TextWrapping="Wrap" />
</StackPanel>

<StackPanel Grid.Column="1">
<TextBlock Text="Bulk edit" FontWeight="SemiBold" Foreground="#102A43" />
<StackPanel Margin="0,8,0,0">
<TextBlock Text="Club" Foreground="#334E68" />
<ComboBox ItemsSource="{Binding Clubs}"
SelectedValue="{Binding SelectedClubId, Mode=TwoWay}"
SelectedValuePath="Id">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
<StackPanel Margin="0,10,0,0">
<TextBlock Text="Swing type" Foreground="#334E68" />
<ComboBox ItemsSource="{Binding SwingTypeOptions}"
DisplayMemberPath="DisplayText"
SelectedValuePath="Value"
SelectedValue="{Binding SelectedSwingType, Mode=TwoWay}" />
</StackPanel>
</StackPanel>
</Grid>
</Border>
</Grid>
</DockPanel>
</Border>
</UserControl>
11 changes: 11 additions & 0 deletions src/CarryIQ.App/Controls/ShotReviewView.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Windows.Controls;

namespace CarryIQ.App;

public partial class ShotReviewView : UserControl
{
public ShotReviewView()
{
InitializeComponent();
}
}
4 changes: 4 additions & 0 deletions src/CarryIQ.App/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@
<local:ShotEntryView />
</DataTemplate>

<DataTemplate DataType="{x:Type local:ShotReviewViewModel}">
<local:ShotReviewView />
</DataTemplate>

<Style x:Key="ShellNavItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Foreground" Value="{StaticResource ShellTextBrush}" />
<Setter Property="Margin" Value="0,0,0,10" />
Expand Down
75 changes: 75 additions & 0 deletions src/CarryIQ.App/ShotReviewRowViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using CommunityToolkit.Mvvm.ComponentModel;

namespace CarryIQ.App;

public sealed partial class ShotReviewRowViewModel : ObservableObject
{
private bool _isSelected;

public ShotReviewRowViewModel(Shot shot, string clubName)
{
Id = shot.Id;
PracticeSessionId = shot.PracticeSessionId;
ShotSequence = shot.ShotSequence;
RecordedAt = shot.RecordedAt;
ClubId = shot.ClubId;
ClubName = clubName;
IsIncluded = shot.IsIncluded;
ExclusionReason = shot.ExclusionReason;
SwingType = shot.SwingType;
Notes = shot.Notes;
RawImportData = shot.RawImportData;
DisplayCarryDistance = shot.CarryDistance?.Yards;
}

public Guid Id { get; }

public Guid PracticeSessionId { get; }

public int ShotSequence { get; }

public DateTimeOffset RecordedAt { get; }

public Guid ClubId { get; private set; }

public string ClubName { get; private set; }

public bool IsIncluded { get; private set; }

public string? ExclusionReason { get; private set; }

public SwingType? SwingType { get; private set; }

public string? Notes { get; private set; }

public string? RawImportData { get; private set; }

public decimal? DisplayCarryDistance { get; }

public bool IsSelected
{
get => _isSelected;
set => SetProperty(ref _isSelected, value);
}

public string InclusionState => IsIncluded ? "Included" : "Excluded";

public void ApplyShot(Shot shot, string clubName)
{
ClubId = shot.ClubId;
ClubName = clubName;
IsIncluded = shot.IsIncluded;
ExclusionReason = shot.ExclusionReason;
SwingType = shot.SwingType;
Notes = shot.Notes;
RawImportData = shot.RawImportData;
OnPropertyChanged(nameof(ClubId));
OnPropertyChanged(nameof(ClubName));
OnPropertyChanged(nameof(IsIncluded));
OnPropertyChanged(nameof(ExclusionReason));
OnPropertyChanged(nameof(SwingType));
OnPropertyChanged(nameof(Notes));
OnPropertyChanged(nameof(RawImportData));
OnPropertyChanged(nameof(InclusionState));
}
}
Loading
Loading