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<BookQueryPage, BookQueryViewModel>();
ViewFactory.Register<BookQueriesPage, BookQueriesViewModel>(); ViewFactory.Register<BookQueriesPage, BookQueriesViewModel>();
ViewFactory.Register<EditBillingLinePage, BillingLineViewModel>(); ViewFactory.Register<EditBillingLinePage, BillingLineViewModel>();
ViewFactory.Register<EditEstimatePage, EstimateViewModel>(); ViewFactory.Register<EditEstimatePage, EditEstimateViewModel>();
ConfigManager = new XLabs.Settings.GenericConfigSettingsMgr(s => ConfigManager = new XLabs.Settings.GenericConfigSettingsMgr(s =>
MainSettings.AppSettings.GetValueOrDefault<string>(s, MainSettings.SettingsDefault), null); 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"> <Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon> <DependentUpon>App.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Attributes\CurrencyAttribute.cs" />
<Compile Include="Attributes\DisplayAttribute.cs" />
<Compile Include="Behaviors\EmailValidatorBehavior.cs" /> <Compile Include="Behaviors\EmailValidatorBehavior.cs" />
<Compile Include="Behaviors\MaxLengthValidator.cs" /> <Compile Include="Behaviors\MaxLengthValidator.cs" />
<Compile Include="Behaviors\DecimalValidatorBehavior.cs" /> <Compile Include="Behaviors\DecimalValidatorBehavior.cs" />
<Compile Include="Behaviors\PickerBehavior.cs" /> <Compile Include="Behaviors\PickerBehavior.cs" />
<Compile Include="Behaviors\StarBehavior.cs" /> <Compile Include="Behaviors\StarBehavior.cs" />
<Compile Include="Constants.cs" /> <Compile Include="Constants.cs" />
<Compile Include="Views\EnumPicker.cs" />
<Compile Include="Converters\BooleanToObjectConverter.cs" /> <Compile Include="Converters\BooleanToObjectConverter.cs" />
<Compile Include="Converters\EnumConverter.cs" /> <Compile Include="Converters\EnumConverter.cs" />
<Compile Include="Converters\GenderConverter.cs" /> <Compile Include="Converters\GenderConverter.cs" />
<Compile Include="Converters\RatingText.cs" /> <Compile Include="Converters\RatingText.cs" />
<Compile Include="Extensions\EnumExtensions.cs" />
<Compile Include="Extensions\ImageResourceExtension.cs" /> <Compile Include="Extensions\ImageResourceExtension.cs" />
<Compile Include="Factories\ViewFactory.cs" /> <Compile Include="Factories\ViewFactory.cs" />
<Compile Include="Helpers\DataManager.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!**" Description = "# **Hello Estimate!**"
}; };
App.NavigationService.NavigateTo<EditEstimatePage>(true, App.NavigationService.NavigateTo<EditEstimatePage>(true,
new EstimateViewModel(e)); new EditEstimateViewModel(e));
} }
} }

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

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

@ -36,17 +36,70 @@
Property=Width, Property=Width,
Factor=1}" Factor=1}"
></views:MarkdownView> ></views:MarkdownView>
<ListView HeightRequest="80" View.VerticalOptions="End">
<ListView.ItemTemplate> <ListView x:Name="BillListView" >
<DataTemplate> <ListView.ItemTemplate RowHeight="20">
<DataTemplate >
<ViewCell> <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> </ViewCell>
</DataTemplate> </DataTemplate>
</ListView.ItemTemplate> </ListView.ItemTemplate>
</ListView> </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> <Button Text="Ajouter une ligne de facture" Clicked="OnNewCommanLine"></Button>
<Label FormattedText="{Binding FormattedTotal}"/> <Label FormattedText="{Binding FormattedTotal}"/>
<Button Text="Valider ce devis"></Button> <Button Text="Valider ce devis"></Button>

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

@ -1,4 +1,5 @@
using BookAStar.Interfaces; using BookAStar.Attributes;
using BookAStar.Interfaces;
using BookAStar.Model.Workflow; using BookAStar.Model.Workflow;
using System; using System;
using System.Globalization; using System.Globalization;
@ -14,12 +15,12 @@ namespace BookAStar.ViewModels
public BillingLineViewModel( BillingLine data) public BillingLineViewModel( BillingLine data)
{ {
this.data = (data == null) ? new BillingLine() : data; this.data = data ?? new BillingLine();
// sets durationValue & unit // sets durationValue & durationUnit
Duration = data.Duration; Duration = data.Duration;
unitaryCostText = data.UnitaryCost.ToString("G",CultureInfo.InvariantCulture);
} }
protected int count;
public int Count public int Count
{ {
get get
@ -29,11 +30,9 @@ namespace BookAStar.ViewModels
set set
{ {
SetProperty<int>(ref count, value, "Count"); data.Count = value;
data.Count = count;
} }
} }
protected string description;
public string Description public string Description
{ {
get get
@ -43,7 +42,6 @@ namespace BookAStar.ViewModels
set set
{ {
SetProperty<string>(ref description, value, "Description");
data.Description = value; data.Description = value;
} }
} }
@ -56,11 +54,10 @@ namespace BookAStar.ViewModels
set set
{ {
SetProperty<decimal>(ref unitaryCost, value, "UnitaryCost");
data.UnitaryCost = value; data.UnitaryCost = value;
UnitaryCostText = value.ToString(unitCostFormat, CultureInfo.InvariantCulture);
} }
} }
protected int durationValue; protected int durationValue;
public int DurationValue public int DurationValue
{ {
@ -75,7 +72,7 @@ namespace BookAStar.ViewModels
data.Duration = this.Duration; data.Duration = this.Duration;
} }
} }
public enum DurationUnits:int public enum DurationUnits:int
{ {
Jours=0, Jours=0,
@ -83,6 +80,9 @@ namespace BookAStar.ViewModels
Minutes=2 Minutes=2
} }
private DurationUnits durationUnit; 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 public DurationUnits DurationUnit
{ {
get { get {
@ -90,8 +90,8 @@ namespace BookAStar.ViewModels
} }
set set
{ {
SetProperty<DurationUnits>(ref durationUnit, value, "DurationUnit"); SetProperty<DurationUnits>(ref durationUnit, value, "DurationUnit");
data.Duration = this.Duration; data.Duration = this.Duration;
} }
} }
@ -106,28 +106,18 @@ namespace BookAStar.ViewModels
} }
set 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 this.UnitaryCost = test;
{
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`
}
} }
SetProperty<string>(ref unitaryCostText, value, "UnitaryCostText");
} }
} }
bool invalidCost;
public bool InvalidCost
{
get { return invalidCost; }
}
public ICommand ValidateCommand { set; get; } public ICommand ValidateCommand { set; get; }
public TimeSpan Duration public TimeSpan Duration

@ -7,9 +7,9 @@ using Xamarin.Forms;
namespace BookAStar.ViewModels namespace BookAStar.ViewModels
{ {
public class EstimateViewModel : ViewModel public class EditEstimateViewModel : ViewModel
{ {
public EstimateViewModel(Estimate data) public EditEstimateViewModel(Estimate data)
{ {
Data = data; Data = data;
if (data.AttachedFiles == null) data.AttachedFiles = new List<string>(); 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…