fixes the estimate line edition

vnext
Paul Schneider 8 years ago
parent 6d049080c9
commit b2efe90218
14 changed files with 336 additions and 69 deletions

@ -111,7 +111,7 @@ namespace BookAStar
ViewFactory.Register<BookQueryPage, BookQueryViewModel>();
ViewFactory.Register<BookQueriesPage, BookQueriesViewModel>();
ViewFactory.Register<EditBillingLinePage, BillingLineViewModel>();
ViewFactory.Register<EditEstimatePage, EstimateViewModel>();
ViewFactory.Register<EditEstimatePage, EditEstimateViewModel>();
ConfigManager = new XLabs.Settings.GenericConfigSettingsMgr(s =>
MainSettings.AppSettings.GetValueOrDefault<string>(s, MainSettings.SettingsDefault), null);

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BookAStar.Attributes
{
class CurrencyAttribute: Attribute
{
}
}

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BookAStar.Attributes
{
class DisplayAttribute : Attribute
{
public string Name { get; set; }
public string Description { get; set; }
public DisplayAttribute()
{
}
public DisplayAttribute(string name)
{
Name = name;
}
}
}

@ -40,16 +40,20 @@
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="Attributes\CurrencyAttribute.cs" />
<Compile Include="Attributes\DisplayAttribute.cs" />
<Compile Include="Behaviors\EmailValidatorBehavior.cs" />
<Compile Include="Behaviors\MaxLengthValidator.cs" />
<Compile Include="Behaviors\DecimalValidatorBehavior.cs" />
<Compile Include="Behaviors\PickerBehavior.cs" />
<Compile Include="Behaviors\StarBehavior.cs" />
<Compile Include="Constants.cs" />
<Compile Include="Views\EnumPicker.cs" />
<Compile Include="Converters\BooleanToObjectConverter.cs" />
<Compile Include="Converters\EnumConverter.cs" />
<Compile Include="Converters\GenderConverter.cs" />
<Compile Include="Converters\RatingText.cs" />
<Compile Include="Extensions\EnumExtensions.cs" />
<Compile Include="Extensions\ImageResourceExtension.cs" />
<Compile Include="Factories\ViewFactory.cs" />
<Compile Include="Helpers\DataManager.cs" />

@ -0,0 +1,22 @@
using System;
using System.Globalization;
using Xamarin.Forms;
namespace BookAStar.Converters
{
/// <summary>
/// When EnumType:int
/// </summary>
class EnumConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int) value;
}
}
}

@ -0,0 +1,43 @@
using BookAStar.Attributes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace BookAStar.Extensions
{
public static class EnumExtensions
{
public static string GetDescription(this Enum value)
{
var type = value.GetType();
var typeInfo = type.GetTypeInfo();
var declaredMember = typeInfo.DeclaredMembers.FirstOrDefault(i => i.Name == value.ToString());
var attribute = declaredMember?.GetCustomAttribute<DisplayAttribute>();
return attribute == null ? value.ToString() : attribute.Description;
}
public static IEnumerable<string> GetDescriptions(Type type)
{
var values = Enum.GetValues(type).Cast<Enum>();
var descriptions = new List<string>();
foreach (var value in values)
{
descriptions.Add(value.GetDescription());
}
return descriptions;
}
public static Enum GetEnumFromDescription(string description, Type enumType)
{
var enumValues = Enum.GetValues(enumType).Cast<Enum>();
var descriptionToEnum = enumValues.ToDictionary(k => k.GetDescription(), v => v);
return descriptionToEnum[description];
}
}
}

@ -78,7 +78,7 @@ namespace BookAStar.Pages
Description = "# **Hello Estimate!**"
};
App.NavigationService.NavigateTo<EditEstimatePage>(true,
new EstimateViewModel(e));
new EditEstimateViewModel(e));
}
}

@ -16,6 +16,7 @@
<Style TargetType="Button">
<Setter Property="Style" Value="{StaticResource ButtonStyle}" />
</Style>
<converters:EnumConverter x:Key="enumToInt" />
<converters:BooleanToObjectConverter x:Key="boolToStyleImage" x:TypeArguments="Style">
<converters:BooleanToObjectConverter.FalseObject>
@ -33,26 +34,29 @@
Value="{extensions:ImageResource BookAStar.Images.Validation.success.png}" />
</Style>
</converters:BooleanToObjectConverter.TrueObject>
</converters:BooleanToObjectConverter>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout x:Name="mainStackLayout">
<Label Text="Description de la ligne de facture"
Style="{StaticResource InputLabelStyle}"></Label>
<Editor VerticalOptions="FillAndExpand"></Editor>
<Editor VerticalOptions="FillAndExpand" Text="{Binding Description, Mode=TwoWay}" ></Editor>
<Label Text="Durée de la prestation"
Style="{StaticResource InputLabelStyle}"></Label>
<StackLayout Orientation="Horizontal">
<Entry Placeholder="Durée" Keyboard="Numeric" Style="{StaticResource BigEntry}" />
<Picker x:Name="picker" SelectedIndex="{Binding UnitaryCost, Mode=TwoWay}"
Style="{StaticResource PickerStyle}" Title="Unité de temps">
<Picker.Items>
<x:String>Jours</x:String>
<x:String>Heures</x:String>
<x:String>Minutes</x:String>
</Picker.Items>
</Picker>
<Entry Placeholder="Durée" Keyboard="Numeric" Style="{StaticResource BigEntry}"
Text="{Binding DurationValue, Mode=TwoWay}" />
<views:EnumPicker x:Name="picker" Style="{StaticResource PickerStyle}"
EnumSource="{Binding DurationUnit}"
Title="Unité de temps"
SelectedItem="{Binding DurationUnit, Mode=TwoWay}">
</views:EnumPicker>
</StackLayout>
<Label Text="Quantité facturée" Style="{StaticResource InputLabelStyle}"></Label>
<Entry Text="{Binding Count, Mode=TwoWay}" Placeholder="Quantité" Keyboard="Numeric"
Style="{StaticResource BigEntry}"/>
@ -63,14 +67,15 @@
<Entry.Behaviors>
<behaviors:DecimalValidatorBehavior x:Name="unitCostValidator" />
</Entry.Behaviors>
<Image x:Name="unitaryCostSuccessErrorImage"
Style="{Binding Source={x:Reference unitCostValidator},
</Entry>
<Label Text="€" Style="{StaticResource BigLabel}" />
<Image x:Name="unitaryCostSuccessErrorImage"
Style="{Binding Source={x:Reference unitCostValidator},
Path=IsValid,
Converter={StaticResource boolToStyleImage}}" />
</Entry>
<Label Text="€" Style="{StaticResource BigLabel}" HorizontalTextAlignment="Start"/>
</StackLayout>
<Button Text="Valider cette ligne de facture" Command="{Binding ValidateCommand}"
<Button Text="Valider cette ligne de devis"
Command="{Binding ValidateCommand}"
Clicked="OnValidateClicked"></Button>
</StackLayout>
</ContentPage>

@ -1,10 +1,6 @@
using BookAStar.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
namespace BookAStar.Pages
@ -13,12 +9,17 @@ namespace BookAStar.Pages
{
public EditBillingLinePage(BillingLineViewModel model)
{
BindingContext = model;
InitializeComponent();
foreach
(string du in Enum.GetNames(typeof(BillingLineViewModel.DurationUnits)))
picker.Items.Add(du);
BindingContext = model;
}
public void OnValidateClicked (object sender, EventArgs e)
{
OnBackButtonPressed();
this.Navigation.PopAsync();
}
}
}

@ -37,16 +37,69 @@
Factor=1}"
></views:MarkdownView>
<ListView HeightRequest="80" View.VerticalOptions="End">
<ListView.ItemTemplate>
<DataTemplate>
<ListView x:Name="BillListView" >
<ListView.ItemTemplate RowHeight="20">
<DataTemplate >
<ViewCell>
<ViewCell.View HeightRequest="80">
</ViewCell.View>
<ViewCell.View>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="30" />
<ColumnDefinition Width="90" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Description}"
Grid.Row="0" Grid.Column="0" ></Label>
<Label Text="{Binding Duration}"
Grid.Row="0" Grid.Column="1" ></Label>
<Label Text="{Binding Count}"
Grid.Row="0" Grid.Column="2" ></Label>
<Label Text="{Binding UnitaryCost}"
Grid.Row="0" Grid.Column="3" ></Label>
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!--
<ListView x:Name="BillListView" View.VerticalOptions="End"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height Factor=.4}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1}">
<ListView.ItemTemplate RowHeight="20">
<DataTemplate >
<ViewCell>
<ViewCell.View>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="30" />
<ColumnDefinition Width="90" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Description}"
Grid.Row="0" Grid.Column="0" ></Label>
<Label Text="{Binding Duration}"
Grid.Row="0" Grid.Column="1" ></Label>
<Label Text="{Binding Count}"
Grid.Row="0" Grid.Column="2" ></Label>
<Label Text="{Binding UnitaryCost}"
Grid.Row="0" Grid.Column="3" ></Label>
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView> -->
<Button Text="Ajouter une ligne de facture" Clicked="OnNewCommanLine"></Button>
<Label FormattedText="{Binding FormattedTotal}"/>
<Button Text="Valider ce devis"></Button>

@ -19,7 +19,7 @@ namespace BookAStar.Pages
public partial class EditEstimatePage : ContentPage
{
public EditEstimatePage(EstimateViewModel model)
public EditEstimatePage(EditEstimateViewModel model)
{
InitializeComponent();
BindingContext = model;
@ -30,14 +30,16 @@ namespace BookAStar.Pages
base.OnBindingContextChanged();
// FIXME WAZA
if (BindingContext != null) {
mdview.Markdown = ((EstimateViewModel)BindingContext).Description;
var e = ((EditEstimateViewModel)BindingContext);
mdview.Markdown = e.Description;
// BillListView.ItemsSource = e.Bill;
}
}
protected void OnDescriptionChanged (object sender, EventArgs e)
{
// FIXME Why the Binding don't work?
((EstimateViewModel)BindingContext).Description = mdview.Markdown;
((EditEstimateViewModel)BindingContext).Description = mdview.Markdown;
InvalidateMeasure();
}
@ -47,7 +49,7 @@ namespace BookAStar.Pages
var lineView = new BillingLineViewModel(com)
{
ValidateCommand = new Command(() => {
((EstimateViewModel)BindingContext).Bill.
((EditEstimateViewModel)BindingContext).Bill.
Add(com);
})
};

@ -1,4 +1,5 @@
using BookAStar.Interfaces;
using BookAStar.Attributes;
using BookAStar.Interfaces;
using BookAStar.Model.Workflow;
using System;
using System.Globalization;
@ -14,12 +15,12 @@ namespace BookAStar.ViewModels
public BillingLineViewModel( BillingLine data)
{
this.data = (data == null) ? new BillingLine() : data;
// sets durationValue & unit
this.data = data ?? new BillingLine();
// sets durationValue & durationUnit
Duration = data.Duration;
unitaryCostText = data.UnitaryCost.ToString("G",CultureInfo.InvariantCulture);
}
protected int count;
public int Count
{
get
@ -29,11 +30,9 @@ namespace BookAStar.ViewModels
set
{
SetProperty<int>(ref count, value, "Count");
data.Count = count;
data.Count = value;
}
}
protected string description;
public string Description
{
get
@ -43,7 +42,6 @@ namespace BookAStar.ViewModels
set
{
SetProperty<string>(ref description, value, "Description");
data.Description = value;
}
}
@ -56,11 +54,10 @@ namespace BookAStar.ViewModels
set
{
SetProperty<decimal>(ref unitaryCost, value, "UnitaryCost");
data.UnitaryCost = value;
UnitaryCostText = value.ToString(unitCostFormat, CultureInfo.InvariantCulture);
}
}
protected int durationValue;
public int DurationValue
{
@ -83,6 +80,9 @@ namespace BookAStar.ViewModels
Minutes=2
}
private DurationUnits durationUnit;
[Display(Name = "Unité de temps", Description = @"Unité de temps utiliée
pour décrire la quantité de travail associée à ce type de service")]
public DurationUnits DurationUnit
{
get {
@ -90,8 +90,8 @@ namespace BookAStar.ViewModels
}
set
{
SetProperty<DurationUnits>(ref durationUnit, value, "DurationUnit");
data.Duration = this.Duration;
SetProperty<DurationUnits>(ref durationUnit, value, "DurationUnit");
data.Duration = this.Duration;
}
}
@ -106,28 +106,18 @@ namespace BookAStar.ViewModels
}
set
{
if (unitaryCostText != value)
SetProperty<string>(ref unitaryCostText, value, "UnitaryCostText");
// TODO update behavior
decimal test;
if (decimal.TryParse(value, NumberStyles.Currency, CultureInfo.InvariantCulture, out test))
{
try
{
data.UnitaryCost = decimal.Parse(value, CultureInfo.InvariantCulture);
}
catch (Exception)
{
// TODO Error model
// UI should shoud entry as wearing a wrong value
// thanks to its `Behaviors`
}
this.UnitaryCost = test;
}
SetProperty<string>(ref unitaryCostText, value, "UnitaryCostText");
}
}
bool invalidCost;
public bool InvalidCost
{
get { return invalidCost; }
}
public ICommand ValidateCommand { set; get; }
public TimeSpan Duration

@ -7,9 +7,9 @@ using Xamarin.Forms;
namespace BookAStar.ViewModels
{
public class EstimateViewModel : ViewModel
public class EditEstimateViewModel : ViewModel
{
public EstimateViewModel(Estimate data)
public EditEstimateViewModel(Estimate data)
{
Data = data;
if (data.AttachedFiles == null) data.AttachedFiles = new List<string>();
@ -105,7 +105,6 @@ namespace BookAStar.ViewModels
}
};
}
set { }
}
}
}

@ -0,0 +1,114 @@
using BookAStar.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace BookAStar.Views
{
/// <summary>
/// A control for picking an element from an enumerated type.
/// </summary>
public class EnumPicker : Picker
{
#region Static Fields
/// <summary>
/// The source used to build values for this picker
/// is any enum value from the desired enum type
/// </summary>
public static BindableProperty EnumSourceProperty =
BindableProperty.Create(
"EnumSource", typeof(Enum), typeof(EnumPicker),
default(Enum), BindingMode.OneWay, propertyChanged: OnEnumSourceChanged);
public static BindableProperty SelectedItemProperty =
BindableProperty.Create("SelectedItem", typeof(Enum), typeof(EnumPicker),
default(Enum), BindingMode.TwoWay, null, propertyChanged: OnSelectedItemChanged);
#endregion
#region Constructors and Destructors
public EnumPicker()
{
this.SelectedIndexChanged += this.OnSelectedIndexChanged;
}
#endregion
#region Public Properties
public Enum EnumSource
{
get
{
return (Enum)this.GetValue(EnumSourceProperty);
}
set
{
this.SetValue(EnumSourceProperty, value);
}
}
public Enum SelectedItem
{
get
{
return (Enum)this.GetValue(SelectedItemProperty);
}
set
{
this.SetValue(SelectedItemProperty, value);
}
}
#endregion
#region Methods
private static void OnEnumSourceChanged(BindableObject bindable, object oldValue, object newValue)
{
var picker = bindable as EnumPicker;
if (picker == null)
{
return;
}
if (newValue == null)
{
return;
}
// Find the values not already contained in the Picker's list of items.
var items = EnumExtensions.GetDescriptions(newValue.GetType());
var toBeAdded = items.Where(item => !picker.Items.Contains(item));
foreach (var item in toBeAdded)
{
picker.Items.Add(item);
}
}
private static void OnSelectedItemChanged(BindableObject bindable, object oldValue, object newValue)
{
var picker = bindable as EnumPicker;
if (newValue == null || oldValue == newValue || picker == null)
{
return;
}
picker.SelectedIndex = picker.Items.IndexOf(((Enum)newValue).GetDescription());
}
private void OnSelectedIndexChanged(object sender, EventArgs eventArgs)
{
var picker = sender as EnumPicker;
if (null == picker || (this.SelectedIndex < 0 || this.SelectedIndex > this.Items.Count - 1))
{
return;
}
var type = this.EnumSource.GetType();
var values = EnumExtensions.GetDescriptions(type);
var items = (from object item in values select item).ToList();
picker.SelectedItem = EnumExtensions.GetEnumFromDescription(items[this.SelectedIndex].ToString(), type);
}
#endregion
}
}
Loading…