diff options
20 files changed, 662 insertions, 41 deletions
diff --git a/Software/DB/Tango.mdf b/Software/DB/Tango.mdf Binary files differindex 51eee6c23..ac922cd3f 100644 --- a/Software/DB/Tango.mdf +++ b/Software/DB/Tango.mdf diff --git a/Software/DB/Tango_log.ldf b/Software/DB/Tango_log.ldf Binary files differindex 399d991f2..0f3890baa 100644 --- a/Software/DB/Tango_log.ldf +++ b/Software/DB/Tango_log.ldf diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/BugReporting/Bug.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/BugReporting/Bug.cs index a1f5f3f86..667c1fd90 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/BugReporting/Bug.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/BugReporting/Bug.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; +using Tango.TFS; namespace Tango.FSE.Common.BugReporting { @@ -12,6 +14,11 @@ namespace Tango.FSE.Common.BugReporting public class Bug { /// <summary> + /// Gets or sets the bug identifier. + /// </summary> + public String URL { get; set; } + + /// <summary> /// Gets or sets the bug type. /// </summary> public BugType Type { get; set; } @@ -40,5 +47,25 @@ namespace Tango.FSE.Common.BugReporting /// Gets or sets optional steps to reproduce. /// </summary> public String StepsToReproduce { get; set; } + + /// <summary> + /// Gets or sets the assigned to team member. + /// </summary> + public TeamMember AssignedTo { get; set; } + + /// <summary> + /// Gets or sets the area. + /// </summary> + public Area Area { get; set; } + + /// <summary> + /// Gets or sets the bug attachments. + /// </summary> + public List<BugAttachement> Attachments { get; set; } + + public Bug() + { + Attachments = new List<BugAttachement>(); + } } } diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/BugReporting/BugAttachement.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/BugReporting/BugAttachement.cs new file mode 100644 index 000000000..71a6cd9f7 --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/BugReporting/BugAttachement.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FSE.Common.BugReporting +{ + public class BugAttachement + { + public String Name { get; set; } + public String Description { get; set; } + public String File { get; set; } + } +} diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Converters/FilePathToIconConverter.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Converters/FilePathToIconConverter.cs new file mode 100644 index 000000000..03df2b6ff --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Converters/FilePathToIconConverter.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; + +namespace Tango.FSE.Common.Converters +{ + public class FilePathToIconConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + try + { + String file = value.ToStringSafe(); + + if (file != null) + { + var shellFile = Microsoft.WindowsAPICodePack.Shell.ShellFile.FromFilePath(file); + var source = shellFile.Thumbnail.MediumBitmapSource; + shellFile.Dispose(); + return source; + } + } + catch { } + + return null; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/FSESettings.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/FSESettings.cs index 6dd2f6d7d..0592aa6cc 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/FSESettings.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/FSESettings.cs @@ -127,6 +127,16 @@ namespace Tango.FSE.Common public bool EnableAdaptiveScaling { get; set; } /// <summary> + /// Gets or sets the last issue report assigned to. + /// </summary> + public String LastReportAssignedTo { get; set; } + + /// <summary> + /// Gets or sets the last report area. + /// </summary> + public String LastReportArea { get; set; } + + /// <summary> /// Initializes a new instance of the <see cref="FSESettings"/> class. /// </summary> public FSESettings() diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/SnackbarItem.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/SnackbarItem.cs index aaefa4fc4..23d16417c 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/SnackbarItem.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/SnackbarItem.cs @@ -107,8 +107,17 @@ namespace Tango.FSE.Common.Notifications } } - public void ProgressCompleted(String message, TimeSpan? timeout = null) + public void ProgressCompleted(String message, TimeSpan? timeout = null, Action pressAction = null) { + if (pressAction != null) + { + _pressAction = () => + { + Close(); + pressAction.Invoke(); + }; + } + IsProgress = false; CanClose = true; Message = message; diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Resources/Converters.xaml b/Software/Visual_Studio/FSE/Tango.FSE.Common/Resources/Converters.xaml index 019206d15..dce8bea15 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Resources/Converters.xaml +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Resources/Converters.xaml @@ -43,4 +43,5 @@ <localConverters:DateTimeUtcHumanizeConverter x:Key="DateTimeUtcHumanizeConverter" /> <localConverters:TimeSpanHumanizeConverter x:Key="TimeSpanHumanizeConverter" /> <localConverters:JobProgressToPositionConverter x:Key="JobProgressToPositionConverter" /> + <localConverters:FilePathToIconConverter x:Key="FilePathToIconConverter" /> </ResourceDictionary>
\ No newline at end of file diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Resources/Styles.xaml b/Software/Visual_Studio/FSE/Tango.FSE.Common/Resources/Styles.xaml index b78f3d029..849590d70 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Resources/Styles.xaml +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Resources/Styles.xaml @@ -150,7 +150,7 @@ <Setter Property="SelectedItemTemplate"> <Setter.Value> <DataTemplate> - <TextBlock Text="{Binding ElementName=PART_Editor,Path=Text}" VerticalAlignment="Center"></TextBlock> + <TextBox IsReadOnly="True" Focusable="False" IsHitTestVisible="False" Text="{Binding ElementName=PART_Editor,Path=Text}"></TextBox> </DataTemplate> </Setter.Value> </Setter> diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Tango.FSE.Common.csproj b/Software/Visual_Studio/FSE/Tango.FSE.Common/Tango.FSE.Common.csproj index e26ccb5bc..39211cb7a 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Tango.FSE.Common.csproj +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Tango.FSE.Common.csproj @@ -71,6 +71,12 @@ <Reference Include="MaterialDesignThemes.Wpf, Version=3.0.1.920, Culture=neutral, processorArchitecture=MSIL"> <HintPath>..\..\packages\MaterialDesignThemes.3.0.1\lib\net45\MaterialDesignThemes.Wpf.dll</HintPath> </Reference> + <Reference Include="Microsoft.WindowsAPICodePack, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\..\packages\WindowsAPICodePack-Core.1.1.1\lib\Microsoft.WindowsAPICodePack.dll</HintPath> + </Reference> + <Reference Include="Microsoft.WindowsAPICodePack.Shell, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\..\packages\WindowsAPICodePack-Shell.1.1.1\lib\Microsoft.WindowsAPICodePack.Shell.dll</HintPath> + </Reference> <Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> <HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath> </Reference> @@ -107,6 +113,7 @@ <Compile Include="Behaviors\CircularProgressBarBehavior.cs" /> <Compile Include="Behaviors\StyleBehavior.cs" /> <Compile Include="BugReporting\Bug.cs" /> + <Compile Include="BugReporting\BugAttachement.cs" /> <Compile Include="BugReporting\BugType.cs" /> <Compile Include="BugReporting\IBugReporter.cs" /> <Compile Include="Connection\FSEMachineEventsStateProvider.cs" /> @@ -141,6 +148,7 @@ <Compile Include="Controls\ToggleIconButton.cs" /> <Compile Include="Converters\DateTimeUtcHumanizeConverter.cs" /> <Compile Include="Converters\DoubleToChartValuesConverter.cs" /> + <Compile Include="Converters\FilePathToIconConverter.cs" /> <Compile Include="Converters\JobProgressToPositionConverter.cs" /> <Compile Include="Converters\TimeSpanHumanizeConverter.cs" /> <Compile Include="Core\FSEProgress.cs" /> @@ -462,6 +470,10 @@ <Project>{8491d07b-c1f6-4b62-a412-41b9fd2d6538}</Project> <Name>Tango.SharedUI</Name> </ProjectReference> + <ProjectReference Include="..\..\Tango.TFS\Tango.TFS.csproj"> + <Project>{998f8471-dc1b-41b6-9d96-354e1b4e7a32}</Project> + <Name>Tango.TFS</Name> + </ProjectReference> <ProjectReference Include="..\..\Tango.Transport\Tango.Transport.csproj"> <Project>{74E700B0-1156-4126-BE40-EE450D3C3026}</Project> <Name>Tango.Transport</Name> diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/packages.config b/Software/Visual_Studio/FSE/Tango.FSE.Common/packages.config index 1644bc722..817883374 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/packages.config +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/packages.config @@ -12,4 +12,6 @@ <package id="MaterialDesignColors" version="1.2.2" targetFramework="net461" /> <package id="MaterialDesignThemes" version="3.0.1" targetFramework="net461" /> <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net461" /> + <package id="WindowsAPICodePack-Core" version="1.1.1" targetFramework="net461" /> + <package id="WindowsAPICodePack-Shell" version="1.1.1" targetFramework="net461" /> </packages>
\ No newline at end of file diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/BugReporting/DefaultBugReporter.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/BugReporting/DefaultBugReporter.cs index cb6ba69c6..c156122f9 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/BugReporting/DefaultBugReporter.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/BugReporting/DefaultBugReporter.cs @@ -1,16 +1,19 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Management; using System.Text; using System.Threading.Tasks; using Tango.BL.Entities; +using Tango.BL.Enumerations; using Tango.Core; using Tango.Core.DI; using Tango.Core.ExtensionMethods; using Tango.Core.Helpers; using Tango.FSE.BL; +using Tango.FSE.Common; using Tango.FSE.Common.Authentication; using Tango.FSE.Common.BugReporting; using Tango.FSE.Common.Connection; @@ -26,7 +29,7 @@ using Tango.TFS; namespace Tango.FSE.UI.BugReporting { [TangoCreateWhenRegistered] - public class DefaultBugReporter : ExtendedObject, IBugReporter + public class DefaultBugReporter : FSEExtendedObject, IBugReporter { private bool _isInitialized; private Project Project; @@ -87,20 +90,45 @@ namespace Tango.FSE.UI.BugReporting item.Title = bug.Title; - item.Area = Project.GetAreaByName("FSE"); + if (bug.Area != null) + { + item.Area = bug.Area; + } + else + { + item.Area = Project.GetAreaByName("FSE"); + } + item.Iteration = Project.Iterations.FirstOrDefault(); - var fseMember = Project.Members.SingleOrDefault(x => x.Email.ToLower() == _bugReportingInfoResponse.UserEmail.ToLower()); + var twineUser = Project.Members.SingleOrDefault(x => x.Email.ToLower() == AuthenticationProvider.CurrentUser.Email); - if (fseMember == null) + if (twineUser != null && bug.AssignedTo != null) { - throw new KeyNotFoundException("Could not locate FSE team member."); + LogManager.Log($"Bug is created by '{twineUser.Email}' and is assigned to '{bug.AssignedTo.Email}'..."); + + item.CreatedBy = twineUser; + item.ChangedBy = twineUser; + item.AuthorizedAs = twineUser; + item.AssignedTo = bug.AssignedTo; + } + else + { + var fseMember = Project.Members.SingleOrDefault(x => x.Email.ToLower() == _bugReportingInfoResponse.UserEmail.ToLower()); + + if (fseMember == null) + { + throw new KeyNotFoundException("Could not locate FSE team member."); + } + + item.CreatedBy = fseMember; + item.ChangedBy = fseMember; + item.AuthorizedAs = fseMember; + item.AssignedTo = bug.AssignedTo != null ? bug.AssignedTo : fseMember; + + LogManager.Log($"Bug is created by '{fseMember.Email}' and is assigned to '{item.AssignedTo.Email}'..."); } - item.CreatedBy = fseMember; - item.ChangedBy = fseMember; - item.AuthorizedAs = fseMember; - item.AssignedTo = fseMember; item.StepsToReproduce = bug.StepsToReproduce.ToStringOrEmpty(); @@ -147,6 +175,19 @@ namespace Tango.FSE.UI.BugReporting } } + foreach (var attachment in bug.Attachments.ToList()) + { + if (File.Exists(attachment.File)) + { + item.Attachments.Add(new Attachment() + { + Description = attachment.Description, + FilePath = attachment.File, + Name = attachment.Name, + }); + } + } + if (MachineProvider.IsConnected) { if (MachineProvider.MachineOperator.DeviceInformation != null) @@ -199,6 +240,8 @@ namespace Tango.FSE.UI.BugReporting LogManager.Log("Uploading work item..."); var workItem = _tfsClient.UploadWorkItem(Project, item).Result; + bug.URL = $"https://twinetfs.visualstudio.com/Tango/_workitems/edit/{workItem.ID}"; + LogManager.Log("Deleting temporary folder..."); tempFolder.Delete(); @@ -213,7 +256,70 @@ namespace Tango.FSE.UI.BugReporting /// <returns></returns> public async Task ShowBugReportDialog(Bug bug) { - var vm = await NotificationProvider.ShowDialog(new BugReportViewVM(bug)); + BugReportViewVM vm = null; + + try + { + if (AuthenticationProvider.CurrentUser.HasPermission(Permissions.FSE_ModifyBugReport)) + { + await Task.Factory.StartNew(() => + { + if (!_isInitialized) + { + using (NotificationProvider.PushTaskItem("Initializing issue reporting service...")) + { + Initialize().GetAwaiter().GetResult(); + } + } + else + { + if (!ConnectivityProvider.CheckOnline()) + { + throw new InternetConnectionException(); + } + } + }); + + TeamMember assignedTo = Project.Members.SingleOrDefault(x => x.Email == Settings.LastReportAssignedTo); + Area area = Project.GetAreaByName(Settings.LastReportArea); + + if (assignedTo == null) + { + assignedTo = Project.Members.SingleOrDefault(x => x.Email.ToLower() == _bugReportingInfoResponse.UserEmail.ToLower()); + } + + if (area == null) + { + area = Project.GetAreaByName("FSE"); + } + + vm = await NotificationProvider.ShowDialog(new BugReportFullViewVM(bug) + { + Areas = Project.Areas.ToList(), + TeamMembers = Project.Members.ToList(), + Area = area, + AssignedTo = assignedTo + }); + + if (vm.DialogResult) + { + Settings.LastReportAssignedTo = (vm as BugReportFullViewVM).AssignedTo.Email; + Settings.LastReportArea = (vm as BugReportFullViewVM).Area.Name; + Settings.Save(); + } + } + else + { + vm = await NotificationProvider.ShowDialog(new BugReportViewVM(bug)); + } + } + catch (Exception ex) + { + LogManager.Log(ex, "Error initializing bug report dialog."); + await NotificationProvider.ShowError($"Error occurred while trying to initialize the bug report.\n{ex.FlattenMessage()}"); + return; + } + if (vm.DialogResult) { var snackbar = NotificationProvider.PushProgressSnackbar("Report Issue", "Submitting report..."); @@ -221,7 +327,18 @@ namespace Tango.FSE.UI.BugReporting try { await SubmitBug(vm.Bug); - snackbar.ProgressCompleted("Issue submitted successfully. Thank you!", TimeSpan.FromSeconds(5)); + + if (AuthenticationProvider.CurrentUser.HasPermission(Permissions.FSE_ModifyBugReport)) + { + snackbar.ProgressCompleted("Issue submitted successfully.\nTap to view the bug report on your browser.", TimeSpan.FromSeconds(8), () => + { + Process.Start(vm.Bug.URL); + }); + } + else + { + snackbar.ProgressCompleted("Issue submitted successfully. Thank you!", TimeSpan.FromSeconds(5)); + } } catch (Exception ex) { diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/BugReportFullView.xaml b/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/BugReportFullView.xaml new file mode 100644 index 000000000..23639c3f7 --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/BugReportFullView.xaml @@ -0,0 +1,129 @@ +<UserControl x:Class="Tango.FSE.UI.Dialogs.BugReportFullView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes" + xmlns:sharedControls="clr-namespace:Tango.SharedUI.Controls;assembly=Tango.SharedUI" + xmlns:controls="clr-namespace:Tango.FSE.Common.Controls;assembly=Tango.FSE.Common" + xmlns:autoComplete="clr-namespace:Tango.AutoComplete.Editors;assembly=Tango.AutoComplete" + xmlns:local="clr-namespace:Tango.FSE.UI.Dialogs" + mc:Ignorable="d" + Width="900" Height="600" d:DataContext="{d:DesignInstance Type=local:BugReportFullViewVM, IsDesignTimeCreatable=False}" Background="{StaticResource FSE_PrimaryBackgroundLightBrush}" Foreground="{StaticResource FSE_PrimaryForegroundBrush}"> + <DockPanel Margin="10"> + + <StackPanel DockPanel.Dock="Top"> + <StackPanel Orientation="Horizontal"> + <material:PackIcon Width="42" Height="42" VerticalAlignment="Center" Kind="Bugfood" Foreground="{StaticResource FSE_ErrorBrush}" /> + <TextBlock VerticalAlignment="Center" Text="Report Issue" Margin="10 0 0 0" FontSize="{StaticResource FSE_LargeFontSize}"></TextBlock> + </StackPanel> + + <TextBlock Margin="0 10 0 0" Foreground="{StaticResource FSE_GrayBrush}" TextWrapping="Wrap"> + <Run>It looks like we have some internal issues.</Run> + <LineBreak/> + <Run>Please provide as much information as you can about this issue you are experiencing.</Run> + </TextBlock> + </StackPanel> + + <Grid Margin="0 20 0 0"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="505*"/> + <ColumnDefinition Width="376*"/> + </Grid.ColumnDefinitions> + <Grid> + <DockPanel> + <StackPanel DockPanel.Dock="Top"> + <TextBox x:Name="txt_title" Margin="0 0 0 0" Text="{Binding Title,NotifyOnValidationError=True,ValidatesOnDataErrors=True,ValidatesOnNotifyDataErrors=True}" material:HintAssist.Hint="Title" material:HintAssist.IsFloating="True"></TextBox> + + <Grid Margin="0 20 0 0"> + <TextBox IsReadOnly="True" Cursor="Hand" material:HintAssist.Hint="Area" material:HintAssist.IsFloating="True" Text="{Binding Area.Name}"></TextBox> + <ToggleButton x:Name="toggleArea"> + <ToggleButton.Template> + <ControlTemplate TargetType="ToggleButton"> + <Grid Background="Transparent"> + <Popup Height="200" StaysOpen="False" AllowsTransparency="True" Width="{TemplateBinding ActualWidth}" PopupAnimation="Slide" IsOpen="{Binding IsChecked, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"> + <Border Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}" Margin="0 2 0 0" BorderBrush="{StaticResource FSE_BorderBrush}" BorderThickness="1" CornerRadius="3"> + <TreeView Loaded="TreeView_Loaded" ItemsSource="{Binding Areas}" SelectedItemChanged="TreeView_SelectedItemChanged"> + <TreeView.ItemTemplate> + <HierarchicalDataTemplate ItemsSource="{Binding SubAreas}"> + <TextBlock Text="{Binding Name}" /> + </HierarchicalDataTemplate> + </TreeView.ItemTemplate> + </TreeView> + </Border> + </Popup> + </Grid> + </ControlTemplate> + </ToggleButton.Template> + </ToggleButton> + </Grid> + + <autoComplete:AutoCompleteTextBox FontSize="{StaticResource FSE_DefaultFontSize}" IconVisibility="Collapsed" Margin="0 20 0 0" Provider="{Binding TeamMembersAutoComplete}" SelectedItem="{Binding AssignedTo,Mode=TwoWay,NotifyOnValidationError=True,ValidatesOnDataErrors=True,ValidatesOnNotifyDataErrors=True}" DisplayMember="AssignName" material:HintAssist.Hint="Assigned To" material:HintAssist.IsFloating="True" VerticalContentAlignment="Bottom"/> + </StackPanel> + + <DockPanel Margin="0 20 0 0"> + <DockPanel DockPanel.Dock="Top"> + <material:PackIcon Kind="Pencil" /> + <TextBlock Margin="5 0 0 0" VerticalAlignment="Center" FontSize="{StaticResource FSE_SmallFontSize}">What Happened ?</TextBlock> + </DockPanel> + <TextBox Background="{StaticResource FSE_PrimaryBackgroundBrush}" Text="{Binding Description,UpdateSourceTrigger=PropertyChanged}" Margin="0 3 0 0" Style="{StaticResource FSE_Rounded_Corners_TextBox_Multiline}" AcceptsReturn="True" TextWrapping="Wrap" Padding="5"></TextBox> + </DockPanel> + </DockPanel> + </Grid> + + <Grid Grid.Column="1" Margin="20 15 0 0"> + <UniformGrid Rows="3"> + <DockPanel> + <DockPanel DockPanel.Dock="Top"> + <material:PackIcon Kind="DebugStepInto" /> + <TextBlock Margin="5 0 0 0" VerticalAlignment="Center" FontSize="{StaticResource FSE_SmallFontSize}">Steps To Reproduce</TextBlock> + </DockPanel> + <TextBox Background="{StaticResource FSE_PrimaryBackgroundBrush}" Text="{Binding StepsToReproduce,UpdateSourceTrigger=PropertyChanged}" Margin="0 3 0 0" Style="{StaticResource FSE_Rounded_Corners_TextBox_Multiline}" AcceptsReturn="True" TextWrapping="Wrap" Padding="5"></TextBox> + </DockPanel> + + <DockPanel Margin="0 20 0 0"> + <DockPanel DockPanel.Dock="Top"> + <material:PackIcon Kind="UserAlertOutline" /> + <TextBlock Margin="5 0 0 0" VerticalAlignment="Center" FontSize="{StaticResource FSE_SmallFontSize}">Additional Comments</TextBlock> + </DockPanel> + <TextBox TextWrapping="Wrap" Text="{Binding Comments,NotifyOnValidationError=True,ValidatesOnDataErrors=True,ValidatesOnNotifyDataErrors=True}" Margin="0 2 0 0" Style="{StaticResource FSE_Rounded_Corners_TextBox_Multiline}" AcceptsReturn="True"></TextBox> + </DockPanel> + + <DockPanel Margin="0 20 0 0"> + <DockPanel DockPanel.Dock="Top"> + <material:PackIcon Kind="Attachment" /> + <UniformGrid Columns="2" DockPanel.Dock="Right"> + <controls:IconButton Command="{Binding RemoveSelectedAttachementsCommand}" Icon="Minus" Foreground="{StaticResource FSE_RedBrush}" Width="20" Height="20" Padding="0" Cursor="Hand" ToolTip="Remove attachment" /> + <controls:IconButton Command="{Binding AddAttachementCommand}" Icon="Plus" Foreground="{StaticResource FSE_GreenBrush}" Width="20" Height="20" Padding="0" Cursor="Hand" ToolTip="Add attachment" /> + </UniformGrid> + <TextBlock Margin="5 0 0 0" VerticalAlignment="Center" FontSize="{StaticResource FSE_SmallFontSize}">Attachments</TextBlock> + </DockPanel> + + <Grid> + <Border CornerRadius="5" Background="{StaticResource FSE_PrimaryBackgroundBrush}" BorderThickness="1" BorderBrush="{StaticResource FSE_BorderBrush}"> + <sharedControls:MultiSelectListBox FocusVisualStyle="{x:Null}" Style="{StaticResource {x:Type ListBox}}" ItemsSource="{Binding Attachments}" SelectedItemsList="{Binding SelectedAttachements}" SelectionMode="Extended"> + <sharedControls:MultiSelectListBox.InputBindings> + <KeyBinding Key="Delete" Command="{Binding RemoveSelectedAttachementsCommand}" /> + </sharedControls:MultiSelectListBox.InputBindings> + <sharedControls:MultiSelectListBox.ItemsPanel> + <ItemsPanelTemplate> + <WrapPanel Orientation="Horizontal" /> + </ItemsPanelTemplate> + </sharedControls:MultiSelectListBox.ItemsPanel> + <sharedControls:MultiSelectListBox.ItemTemplate> + <DataTemplate> + <DockPanel Width="85" Height="72"> + <TextBlock FontSize="{StaticResource FSE_SmallerFontSize}" DockPanel.Dock="Bottom" Margin="0 5 0 0" Text="{Binding Name}" TextAlignment="Center"></TextBlock> + <Image Source="{Binding File,Converter={StaticResource FilePathToIconConverter}}"></Image> + </DockPanel> + </DataTemplate> + </sharedControls:MultiSelectListBox.ItemTemplate> + </sharedControls:MultiSelectListBox> + </Border> + </Grid> + </DockPanel> + </UniformGrid> + </Grid> + </Grid> + </DockPanel> +</UserControl> diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/BugReportFullView.xaml.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/BugReportFullView.xaml.cs new file mode 100644 index 000000000..ac4aeb2ef --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/BugReportFullView.xaml.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using Tango.TFS; + +namespace Tango.FSE.UI.Dialogs +{ + /// <summary> + /// Interaction logic for BugReportView.xaml + /// </summary> + public partial class BugReportFullView : UserControl + { + private BugReportFullViewVM _vm; + private TreeView tree; + + public BugReportFullView() + { + InitializeComponent(); + + this.Loaded += ReportIssueView_Loaded; + this.DataContextChanged += ReportIssueView_DataContextChanged; + } + + private void ReportIssueView_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) + { + _vm = this.DataContext as BugReportFullViewVM; + } + + private void ReportIssueView_Loaded(object sender, RoutedEventArgs e) + { + txt_title.Focus(); + } + + private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e) + { + _vm.Area = e.NewValue as Area; + toggleArea.IsChecked = false; + } + + private void TreeView_Loaded(object sender, RoutedEventArgs e) + { + tree = sender as TreeView; + tree.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged; + } + + private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e) + { + if (tree.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated) + { + TreeViewItem i = tree.ItemContainerGenerator.ContainerFromIndex(0) as TreeViewItem; + i.IsExpanded = true; + } + } + } +} diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/BugReportFullViewVM.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/BugReportFullViewVM.cs new file mode 100644 index 000000000..0f7bb2177 --- /dev/null +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/BugReportFullViewVM.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.Core.Commands; +using Tango.Core.DI; +using Tango.Core.Helpers; +using Tango.FSE.Common; +using Tango.FSE.Common.AutoComplete; +using Tango.FSE.Common.BugReporting; +using Tango.FSE.Common.Notifications; +using Tango.FSE.Common.Storage; +using Tango.TFS; + +namespace Tango.FSE.UI.Dialogs +{ + public class BugReportFullViewVM : BugReportViewVM + { + [TangoInject] + private INotificationProvider NotificationProvider { get; set; } + + [TangoInject] + private IStorageProvider StorageProvider { get; set; } + + public List<Area> Areas { get; set; } + + private Area _area; + [Required(ErrorMessage = "Area is required")] + public Area Area + { + get { return _area; } + set { _area = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } + } + + public List<TeamMember> TeamMembers { get; set; } + + public AutoCompleteSource<TeamMember> TeamMembersAutoComplete { get; set; } + + private TeamMember _assignedTo; + [Required(ErrorMessage = "Assigned To is required")] + public TeamMember AssignedTo + { + get { return _assignedTo; } + set { _assignedTo = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } + } + + private String _stepsToReproduce; + public String StepsToReproduce + { + get { return _stepsToReproduce; } + set { _stepsToReproduce = value; RaisePropertyChangedAuto(); } + } + + public ObservableCollection<BugAttachement> Attachments { get; set; } + public ObservableCollection<BugAttachement> SelectedAttachements { get; set; } + + public RelayCommand AddAttachementCommand { get; set; } + public RelayCommand RemoveSelectedAttachementsCommand { get; set; } + + public BugReportFullViewVM() : base() + { + Attachments = new ObservableCollection<BugAttachement>(); + SelectedAttachements = new ObservableCollection<BugAttachement>(); + SelectedAttachements.CollectionChanged += (_, __) => InvalidateRelayCommands(); + TeamMembersAutoComplete = new AutoCompleteSource<TeamMember>(FilterTeamMembers); + AddAttachementCommand = new RelayCommand(AddAttachement); + RemoveSelectedAttachementsCommand = new RelayCommand(RemoveSelectedAttachements, () => SelectedAttachements.Count > 0); + } + + public BugReportFullViewVM(Bug bug) : this() + { + if (bug != null) + { + Bug = bug; + Title = Bug.Title; + Description = Bug.Description; + } + } + + private List<TeamMember> FilterTeamMembers(string filter) + { + if (TeamMembers != null) + { + return TeamMembers.Where(x => String.IsNullOrWhiteSpace(filter) || x.DisplayName.ToLower().Contains(filter.ToLower())).ToList(); + } + + return new List<TeamMember>(); + } + + private async void AddAttachement() + { + var result = await StorageProvider.OpenFiles("Select file/s to attach"); + + if (result.Confirmed) + { + foreach (var file in result.SelectedItems) + { + String fileName = Path.GetFileName(file); + + if (new FileInfo(file).Length > 1000000 * 60) // 60MB limit + { + await NotificationProvider.ShowError($"The size of '{fileName}' exceeds the maximum 60 MB limit."); + continue; + } + + Attachments.Add(new BugAttachement() + { + Name = fileName, + File = file, + Description = fileName + }); + } + + InvalidateRelayCommands(); + } + } + + private void RemoveSelectedAttachements() + { + foreach (var attachement in SelectedAttachements.ToList()) + { + Attachments.Remove(attachement); + } + + SelectedAttachements.Clear(); + + InvalidateRelayCommands(); + } + + protected override void Accept() + { + if (Validate()) + { + Bug.Title = Title; + Bug.Description = Description; + Bug.Comments = Comments; + Bug.StepsToReproduce = StepsToReproduce; + Bug.AssignedTo = AssignedTo; + Bug.Area = Area; + Bug.Attachments = Attachments.ToList(); + + base.Accept(); + } + } + } +} diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/BugReportView.xaml b/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/BugReportView.xaml index a70b2beb9..0d6a4e75a 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/BugReportView.xaml +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/BugReportView.xaml @@ -6,7 +6,7 @@ xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:local="clr-namespace:Tango.FSE.UI.Dialogs" mc:Ignorable="d" - Width="700" Height="600" d:DataContext="{d:DesignInstance Type=local:BugReportViewVM, IsDesignTimeCreatable=False}" Background="{StaticResource FSE_PrimaryBackgroundLightBrush}" Foreground="{StaticResource FSE_PrimaryForegroundBrush}"> + Width="550" Height="600" d:DataContext="{d:DesignInstance Type=local:BugReportViewVM, IsDesignTimeCreatable=False}" Background="{StaticResource FSE_PrimaryBackgroundLightBrush}" Foreground="{StaticResource FSE_PrimaryForegroundBrush}"> <DockPanel Margin="10"> <StackPanel DockPanel.Dock="Top" Orientation="Horizontal"> @@ -24,14 +24,14 @@ <Run>Help us improve your experience by submitting a report and our team will be notified.</Run> </TextBlock> - <TextBox Margin="0 20" IsReadOnly="{Binding IsEditable,Converter={StaticResource BooleanInverseConverter}}" Text="{Binding Bug.Title}" material:HintAssist.Hint="Title" material:HintAssist.IsFloating="True"></TextBox> + <TextBox Margin="0 20" IsReadOnly="{Binding IsEditable,Converter={StaticResource BooleanInverseConverter}}" Text="{Binding Title,NotifyOnValidationError=True,ValidatesOnDataErrors=True,ValidatesOnNotifyDataErrors=True}" material:HintAssist.Hint="Title" material:HintAssist.IsFloating="True"></TextBox> <StackPanel Margin="0 10 0 0"> <DockPanel> <material:PackIcon Kind="Pencil" /> <TextBlock Margin="5 0 0 0" VerticalAlignment="Center" FontSize="{StaticResource FSE_SmallFontSize}">What Happened ?</TextBlock> </DockPanel> - <TextBox Background="{StaticResource FSE_PrimaryBackgroundBrush}" IsEnabled="{Binding IsEditable}" Text="{Binding Bug.Description,UpdateSourceTrigger=PropertyChanged}" Margin="0 3 0 0" Style="{StaticResource FSE_Rounded_Corners_TextBox_Multiline}" AcceptsReturn="True" TextWrapping="Wrap" Height="200"></TextBox> + <TextBox Background="{StaticResource FSE_PrimaryBackgroundBrush}" IsEnabled="{Binding IsEditable}" Text="{Binding Description,NotifyOnValidationError=True,ValidatesOnDataErrors=True,ValidatesOnNotifyDataErrors=True}" Margin="0 3 0 0" Style="{StaticResource FSE_Rounded_Corners_TextBox_Multiline}" AcceptsReturn="True" TextWrapping="Wrap" Height="200"></TextBox> </StackPanel> </StackPanel> @@ -40,7 +40,7 @@ <material:PackIcon Kind="UserAlertOutline" /> <TextBlock Margin="5 0 0 0" VerticalAlignment="Center" FontSize="{StaticResource FSE_SmallFontSize}">Additional Comments</TextBlock> </DockPanel> - <TextBox TextWrapping="Wrap" Text="{Binding Bug.Comments,UpdateSourceTrigger=PropertyChanged}" Margin="0 2 0 0" Style="{StaticResource FSE_Rounded_Corners_TextBox_Multiline}" AcceptsReturn="True"></TextBox> + <TextBox TextWrapping="Wrap" Text="{Binding Comments,NotifyOnValidationError=True,ValidatesOnDataErrors=True,ValidatesOnNotifyDataErrors=True}" Margin="0 2 0 0" Style="{StaticResource FSE_Rounded_Corners_TextBox_Multiline}" AcceptsReturn="True"></TextBox> </DockPanel> </DockPanel> </Grid> diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/BugReportViewVM.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/BugReportViewVM.cs index 397510ef2..0ba6a6c16 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/BugReportViewVM.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/BugReportViewVM.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -18,6 +19,28 @@ namespace Tango.FSE.UI.Dialogs public Bug Bug { get; set; } public bool IsEditable { get; set; } + private String _title; + [Required(ErrorMessage = "Title is required")] + public String Title + { + get { return _title; } + set { _title = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } + } + + private String _description; + public String Description + { + get { return _description; } + set { _description = value; RaisePropertyChangedAuto(); } + } + + private String _comments; + public String Comments + { + get { return _comments; } + set { _comments = value; RaisePropertyChangedAuto(); } + } + public BugReportViewVM() { TangoIOC.Default.Inject(this); @@ -25,7 +48,7 @@ namespace Tango.FSE.UI.Dialogs Bug = new Bug(); CanClose = true; OKText = "SUBMIT"; - HasDefault = true; + HasDefault = false; } public BugReportViewVM(Bug bug) : this() @@ -33,20 +56,22 @@ namespace Tango.FSE.UI.Dialogs if (bug != null) { Bug = bug; + Title = Bug.Title; + Description = Bug.Description; IsEditable = false; } } protected override void Accept() { - if (Bug.Title.IsNotNullOrEmpty()) + if (Validate()) { + Bug.Title = Title; + Bug.Description = Description; + Bug.Comments = Comments; + base.Accept(); } - else - { - NotificationProvider.ShowError("Report title cannot remain empty."); - } } } } diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Tango.FSE.UI.csproj b/Software/Visual_Studio/FSE/Tango.FSE.UI/Tango.FSE.UI.csproj index 8223bb3d2..54121b9d3 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Tango.FSE.UI.csproj +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Tango.FSE.UI.csproj @@ -229,9 +229,13 @@ <DependentUpon>ApplicationUpdateView.xaml</DependentUpon> </Compile> <Compile Include="Dialogs\ApplicationUpdateViewVM.cs" /> + <Compile Include="Dialogs\BugReportFullView.xaml.cs"> + <DependentUpon>BugReportFullView.xaml</DependentUpon> + </Compile> <Compile Include="Dialogs\BugReportView.xaml.cs"> <DependentUpon>BugReportView.xaml</DependentUpon> </Compile> + <Compile Include="Dialogs\BugReportFullViewVM.cs" /> <Compile Include="Dialogs\BugReportViewVM.cs" /> <Compile Include="Dialogs\MachineConnectionBaseViewVM.cs" /> <Compile Include="Dialogs\MachineConnectionEmulatorView.xaml.cs"> @@ -381,6 +385,10 @@ <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> + <Page Include="Dialogs\BugReportFullView.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </Page> <Page Include="Dialogs\BugReportView.xaml"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> diff --git a/Software/Visual_Studio/SideChains/Tango.AutoComplete/Editors/AutoCompleteTextBox.cs b/Software/Visual_Studio/SideChains/Tango.AutoComplete/Editors/AutoCompleteTextBox.cs index 7ed4814d3..78e62d399 100644 --- a/Software/Visual_Studio/SideChains/Tango.AutoComplete/Editors/AutoCompleteTextBox.cs +++ b/Software/Visual_Studio/SideChains/Tango.AutoComplete/Editors/AutoCompleteTextBox.cs @@ -274,28 +274,33 @@ namespace Tango.AutoComplete.Editors act = d as AutoCompleteTextBox; if (act != null) { - if (act.Editor != null & !act._isUpdatingText) - { - act._isUpdatingText = true; - act.Editor.Text = act.BindingEvaluator.Evaluate(e.NewValue); - Keyboard.ClearFocus(); + act.InvalidateSelection(); + } + } + + private void InvalidateSelection() + { + if (Editor != null & !_isUpdatingText) + { + _isUpdatingText = true; + Editor.Text = BindingEvaluator.Evaluate(SelectedItem); + Keyboard.ClearFocus(); - if (act._partSelectedContent != null) + if (_partSelectedContent != null) + { + if (SelectedItem == null) { - if (act.SelectedItem == null) - { - act._partSelectedContent.Visibility = Visibility.Collapsed; - act.Editor.Foreground = Application.Current.Resources["FSE_PrimaryForegroundBrush"] as Brush; - } - else - { - act._partSelectedContent.Visibility = Visibility.Visible; - act.Editor.Foreground = Brushes.Transparent; - } + _partSelectedContent.Visibility = Visibility.Collapsed; + Editor.Foreground = Application.Current.Resources["FSE_PrimaryForegroundBrush"] as Brush; + } + else + { + _partSelectedContent.Visibility = Visibility.Visible; + Editor.Foreground = Brushes.Transparent; } - - act._isUpdatingText = false; } + + _isUpdatingText = false; } } @@ -350,6 +355,8 @@ namespace Tango.AutoComplete.Editors } _isUpdatingText = false; + + InvalidateSelection(); } private void ItemsSelector_PreviewMouseDown(object sender, MouseButtonEventArgs e) { diff --git a/Software/Visual_Studio/Tango.BL/Enumerations/Permissions.cs b/Software/Visual_Studio/Tango.BL/Enumerations/Permissions.cs index 626031872..d16aa37d0 100644 --- a/Software/Visual_Studio/Tango.BL/Enumerations/Permissions.cs +++ b/Software/Visual_Studio/Tango.BL/Enumerations/Permissions.cs @@ -253,5 +253,11 @@ namespace Tango.BL.Enumerations [Description("Allows viewing FSE full exception details")] FSE_ViewFullExceptionDetails = 1017, + /// <summary> + /// (Allows the modification of bug reports) + /// </summary> + [Description("Allows the modification of bug reports")] + FSE_ModifyBugReport = 1018, + } } |
