diff --git a/BookAStar.sln b/BookAStar.sln index 622d32c9..55bf0575 100644 --- a/BookAStar.sln +++ b/BookAStar.sln @@ -9,7 +9,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookAStar.iOS", "BookAStar\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookAStar", "BookAStar\BookAStar\BookAStar.csproj", "{A0815650-0A0A-47B0-8826-771F0E1AD137}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yavsc.Client", "Yavsc.Client\Yavsc.Client.csproj", "{67F9D3A8-F71E-4428-913F-C37AE82CDB24}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YavscLib", "YavscLib\YavscLib.csproj", "{67F9D3A8-F71E-4428-913F-C37AE82CDB24}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/BookAStar/BookAStar.Droid/BookAStar.Droid.csproj b/BookAStar/BookAStar.Droid/BookAStar.Droid.csproj index 07016d17..c35681fe 100644 --- a/BookAStar/BookAStar.Droid/BookAStar.Droid.csproj +++ b/BookAStar/BookAStar.Droid/BookAStar.Droid.csproj @@ -387,9 +387,9 @@ - + {67f9d3a8-f71e-4428-913f-c37ae82cdb24} - Yavsc.Client + YavscLib {A0815650-0A0A-47B0-8826-771F0E1AD137} diff --git a/BookAStar/BookAStar.iOS/BookAStar.iOS.csproj b/BookAStar/BookAStar.iOS/BookAStar.iOS.csproj index 59e07669..4199662c 100644 --- a/BookAStar/BookAStar.iOS/BookAStar.iOS.csproj +++ b/BookAStar/BookAStar.iOS/BookAStar.iOS.csproj @@ -171,10 +171,6 @@ - - {67F9D3A8-F71E-4428-913F-C37AE82CDB24} - Yavsc.Client - {A0815650-0A0A-47B0-8826-771F0E1AD137} BookAStar diff --git a/BookAStar/BookAStar/App.xaml b/BookAStar/BookAStar/App.xaml index 31426f3f..51ed8d51 100644 --- a/BookAStar/BookAStar/App.xaml +++ b/BookAStar/BookAStar/App.xaml @@ -16,6 +16,8 @@ #000000 #000000 #000000 + #500000 + Black Blue #5050ff @@ -43,9 +45,16 @@ + + + + + + + + + + + + + - - + - + @@ -35,9 +55,7 @@ + Markdown="{Binding Description, Mode=TwoWay}"> @@ -45,13 +63,12 @@ + HasUnevenRows="true" ItemTapped="OnEditLine"> - - + + @@ -60,6 +77,7 @@ + @@ -70,6 +88,9 @@ + @@ -83,6 +104,5 @@ - \ No newline at end of file diff --git a/BookAStar/BookAStar/Pages/EstimatePages/EditEstimatePage.xaml.cs b/BookAStar/BookAStar/Pages/EstimatePages/EditEstimatePage.xaml.cs index 8c4ceabb..4f402d9e 100644 --- a/BookAStar/BookAStar/Pages/EstimatePages/EditEstimatePage.xaml.cs +++ b/BookAStar/BookAStar/Pages/EstimatePages/EditEstimatePage.xaml.cs @@ -57,11 +57,20 @@ namespace BookAStar.Pages protected void OnEditLine(object sender, ItemTappedEventArgs e) { var line = (BillingLineViewModel)e.Item; + // update the validation command, that + // was creating a new line in the bill at creation time, + // now one only wants to update the line line.ValidateCommand = new Command(() => { DataManager.Current.EstimationCache.SaveEntity(); }); - + // and setup a removal command, that was not expected at creation time + var evm = (EditEstimateViewModel)BindingContext; + line.RemoveCommand = new Command(() => + { + evm.Bill.Remove(line); + DataManager.Current.EstimationCache.SaveEntity(); + }); App.NavigationService.NavigateTo( true, line ); } @@ -78,7 +87,9 @@ namespace BookAStar.Pages } await thisPage.Navigation.PopAsync(); }); - var response = await App.DisplayActionSheet(Strings.SignOrNot, Strings.DonotsignEstimate, Strings.CancelValidation, new string[] { Strings.Sign }); + var response = await App.DisplayActionSheet( + Strings.SignOrNot, Strings.DonotsignEstimate, + Strings.CancelValidation, new string[] { Strings.Sign }); if (response == Strings.Sign) { App.NavigationService.NavigateTo(true, diff --git a/BookAStar/BookAStar/Strings.Designer.cs b/BookAStar/BookAStar/Strings.Designer.cs index 9da4009c..f05e5e86 100644 --- a/BookAStar/BookAStar/Strings.Designer.cs +++ b/BookAStar/BookAStar/Strings.Designer.cs @@ -142,6 +142,42 @@ namespace BookAStar { } } + /// + /// Recherche une chaîne localisée semblable à Valeur invalide. + /// + public static string InvalidValue { + get { + return ResourceManager.GetString("InvalidValue", resourceCulture); + } + } + + /// + /// Recherche une chaîne localisée semblable à Veuillez saisir une valeur entre {0} et {1}.. + /// + public static string MinMaxIntError { + get { + return ResourceManager.GetString("MinMaxIntError", resourceCulture); + } + } + + /// + /// Recherche une chaîne localisée semblable à Veuillez saisir une description (entre {0} et {1} caractères).. + /// + public static string MinMaxStringValidationError { + get { + return ResourceManager.GetString("MinMaxStringValidationError", resourceCulture); + } + } + + /// + /// Recherche une chaîne localisée semblable à Pas de description. + /// + public static string NoDescription { + get { + return ResourceManager.GetString("NoDescription", resourceCulture); + } + } + /// /// Recherche une chaîne localisée semblable à Un artiste. /// @@ -231,5 +267,14 @@ namespace BookAStar { return ResourceManager.GetString("ViewEstimate", resourceCulture); } } + + /// + /// Recherche une chaîne localisée semblable à Votre texte a été taillé car il était trop long.. + /// + public static string YourTextWasTooLong { + get { + return ResourceManager.GetString("YourTextWasTooLong", resourceCulture); + } + } } } diff --git a/BookAStar/BookAStar/Strings.resx b/BookAStar/BookAStar/Strings.resx index ae3b7e73..55627f57 100644 --- a/BookAStar/BookAStar/Strings.resx +++ b/BookAStar/BookAStar/Strings.resx @@ -176,4 +176,19 @@ La mise à jour a échoué. + + Veuillez saisir une description (entre {0} et {1} caractères). + + + Votre texte a été taillé car il était trop long. + + + Pas de description + + + Valeur invalide + + + Veuillez saisir une valeur entre {0} et {1}. + \ No newline at end of file diff --git a/BookAStar/BookAStar/ViewModels/EditingViewModel.cs b/BookAStar/BookAStar/ViewModels/EditingViewModel.cs deleted file mode 100644 index 846f518c..00000000 --- a/BookAStar/BookAStar/ViewModels/EditingViewModel.cs +++ /dev/null @@ -1,56 +0,0 @@ -using BookAStar.Data; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using XLabs.Forms.Mvvm; - -namespace BookAStar.ViewModels -{ - /// - /// Used to make the DataManager know how - /// to sync local and remote data - /// - public class EditingViewModel: ViewModel - { - bool existsRemotely; - public bool ExistsRemotely - { - get - { - return existsRemotely; - } - set - { - base.SetProperty(ref existsRemotely, value); - } - } - - bool isValid; - public bool IsValid - { - get - { - return isValid; - } - set - { - base.SetProperty(ref isValid, value); - } - } - - bool isDirty; - public bool IsDirty - { - get - { - return isDirty; - } - set - { - base.SetProperty(ref isDirty, value); - } - } - } -} diff --git a/BookAStar/BookAStar/ViewModels/EstimateAndBilling/BillingLineViewModel.cs b/BookAStar/BookAStar/ViewModels/EstimateAndBilling/BillingLineViewModel.cs index 3afb08af..4b3d8c9c 100644 --- a/BookAStar/BookAStar/ViewModels/EstimateAndBilling/BillingLineViewModel.cs +++ b/BookAStar/BookAStar/ViewModels/EstimateAndBilling/BillingLineViewModel.cs @@ -1,19 +1,54 @@ using BookAStar.Attributes; using BookAStar.Interfaces; using BookAStar.Model.Workflow; +using BookAStar.ViewModels.Validation; using System; using System.Globalization; using System.Windows.Input; +using System.ComponentModel; namespace BookAStar.ViewModels.EstimateAndBilling { - public class BillingLineViewModel : EditingViewModel, IBillingLine + public class BillingLineViewModel : EditingViewModel, IBillingLine { - BillingLine data; + public ICommand RemoveCommand { get; set; } + public ICommand ValidateCommand { set; get; } - public BillingLineViewModel(BillingLine data) + public BillingLineViewModel(BillingLine data): base(data) { - this.Data = data; + CheckCommand = new Action( + (l,s) => { + if (string.IsNullOrWhiteSpace(l.Description)) + { + s.AddError("Description",Strings.NoDescription); + } + if (l.UnitaryCost < 0) { s.AddError("UnitaryCost", Strings.InvalidValue); } + if (l.Count < 0) { s.AddError("Count", Strings.InvalidValue); } + }); + SyncData(); + } + + private void SyncData() + { + if (Data != null) + { + // set durationValue, durationUnit + Duration = Data.Duration; + // other redondant representation + count = Data.Count; + description = Data.Description; + unitaryCostText = Data.UnitaryCost.ToString("G", CultureInfo.InvariantCulture); + } + CheckCommand(Data, ViewModelState); + } + + protected override void OnPropertyChanged(PropertyChangedEventArgs e) + { + base.OnPropertyChanged(e); + if (e.PropertyName=="Data") + { + SyncData(); + } } private int count; @@ -27,7 +62,7 @@ namespace BookAStar.ViewModels.EstimateAndBilling set { SetProperty(ref count, value); - data.Count = count; + Data.Count = count; } } private string description; @@ -41,10 +76,10 @@ namespace BookAStar.ViewModels.EstimateAndBilling set { SetProperty(ref description, value); - data.Description = description; - IsValid = !string.IsNullOrWhiteSpace(description); + Data.Description = description; } } + decimal unitaryCost; public decimal UnitaryCost { @@ -56,7 +91,7 @@ namespace BookAStar.ViewModels.EstimateAndBilling set { SetProperty(ref unitaryCost, value); - data.UnitaryCost = unitaryCost; + Data.UnitaryCost = unitaryCost; } } @@ -71,7 +106,7 @@ namespace BookAStar.ViewModels.EstimateAndBilling set { SetProperty(ref durationValue, value, "DurationValue"); - data.Duration = this.Duration; + Data.Duration = this.Duration; } } @@ -94,7 +129,7 @@ pour décrire la quantité de travail associée à ce type de service")] set { SetProperty(ref durationUnit, value, "DurationUnit"); - data.Duration = this.Duration; + Data.Duration = this.Duration; } } @@ -120,7 +155,6 @@ pour décrire la quantité de travail associée à ce type de service")] } } - public ICommand ValidateCommand { set; get; } public TimeSpan Duration { @@ -161,26 +195,5 @@ pour décrire la quantité de travail associée à ce type de service")] } } - public BillingLine Data - { - get - { - return data; - } - - set - { - SetProperty(ref data, value); - if (data != null) - { - // sets durationValue & durationUnit - count = data.Count; - description = data.Description; - - Duration = data.Duration; - unitaryCostText = data.UnitaryCost.ToString("G", CultureInfo.InvariantCulture); - } - } - } } } diff --git a/BookAStar/BookAStar/ViewModels/EstimateAndBilling/EditEstimateViewModel.cs b/BookAStar/BookAStar/ViewModels/EstimateAndBilling/EditEstimateViewModel.cs index ccbf231f..f02d087e 100644 --- a/BookAStar/BookAStar/ViewModels/EstimateAndBilling/EditEstimateViewModel.cs +++ b/BookAStar/BookAStar/ViewModels/EstimateAndBilling/EditEstimateViewModel.cs @@ -6,18 +6,13 @@ using Xamarin.Forms; using BookAStar.Data; using Newtonsoft.Json; using System.Linq; +using BookAStar.ViewModels.Validation; +using System.ComponentModel; namespace BookAStar.ViewModels.EstimateAndBilling { - public class EditEstimateViewModel : EditingViewModel + public class EditEstimateViewModel : EditingViewModel { - /// - /// For deserialization - /// - public EditEstimateViewModel() - { - - } /// /// Builds a new view model on estimate, @@ -25,11 +20,37 @@ namespace BookAStar.ViewModels.EstimateAndBilling /// /// /// - public EditEstimateViewModel(Estimate data) + public EditEstimateViewModel(Estimate data) : base(data) + { + SyncData(); + } + + private void SyncData() { - Data = data; + if (Data.AttachedFiles == null) Data.AttachedFiles = new List(); + if (Data.AttachedGraphics == null) Data.AttachedGraphics = new List(); + if (Data.Bill == null) Data.Bill = new List(); + AttachedFiles = new ObservableCollection(Data.AttachedFiles); + AttachedGraphicList = new ObservableCollection(Data.AttachedGraphics); + Bill = new ObservableCollection(Data.Bill.Select( + l => new BillingLineViewModel(l) + )); + Bill.CollectionChanged += Bill_CollectionChanged; + Title = Data.Title; + Description = Data.Description; + NotifyPropertyChanged("FormattedTotal"); + NotifyPropertyChanged("Query"); + NotifyPropertyChanged("CLient"); } + protected override void OnPropertyChanged(PropertyChangedEventArgs e) + { + base.OnPropertyChanged(e); + if (e.PropertyName == "Data") + { + SyncData(); + } + } /// /// /// @@ -42,25 +63,6 @@ namespace BookAStar.ViewModels.EstimateAndBilling NotifyPropertyChanged("Bill"); } - private Estimate data; - public Estimate Data { get { return data; } set { - SetProperty(ref data, value); - if (data.AttachedFiles == null) data.AttachedFiles = new List(); - if (data.AttachedGraphics == null) data.AttachedGraphics = new List(); - if (data.Bill == null) data.Bill = new List(); - AttachedFiles = new ObservableCollection(data.AttachedFiles); - AttachedGraphicList = new ObservableCollection(data.AttachedGraphics); - Bill = new ObservableCollection(data.Bill.Select( - l => new BillingLineViewModel(l) - )); - Bill.CollectionChanged += Bill_CollectionChanged; - Title = Data.Title; - Description = Data.Description; - NotifyPropertyChanged("FormattedTotal"); - NotifyPropertyChanged("Query"); - NotifyPropertyChanged("CLient"); - } } - [JsonIgnore] public ObservableCollection AttachedFiles { @@ -124,7 +126,7 @@ namespace BookAStar.ViewModels.EstimateAndBilling { get { - OnPlatform lfs = (OnPlatform)App.Current.Resources["LargeFontSize"]; + OnPlatform lfs = (OnPlatform)App.Current.Resources["MediumFontSize"]; OnPlatform etc = (OnPlatform)App.Current.Resources["EmphasisTextColor"]; return new FormattedString diff --git a/BookAStar/BookAStar/Model/UI/PageState.cs b/BookAStar/BookAStar/ViewModels/PageState.cs similarity index 87% rename from BookAStar/BookAStar/Model/UI/PageState.cs rename to BookAStar/BookAStar/ViewModels/PageState.cs index 186da6da..91509c0a 100644 --- a/BookAStar/BookAStar/Model/UI/PageState.cs +++ b/BookAStar/BookAStar/ViewModels/PageState.cs @@ -1,6 +1,6 @@ using XLabs.Forms.Mvvm; -namespace BookAStar.Model.UI +namespace BookAStar.ViewModels { internal class PageState { diff --git a/BookAStar/BookAStar/ViewModels/Signing/EstimateSigningViewModel.cs b/BookAStar/BookAStar/ViewModels/Signing/EstimateSigningViewModel.cs index a69ad837..a2035b24 100644 --- a/BookAStar/BookAStar/ViewModels/Signing/EstimateSigningViewModel.cs +++ b/BookAStar/BookAStar/ViewModels/Signing/EstimateSigningViewModel.cs @@ -6,9 +6,8 @@ namespace BookAStar.ViewModels.Signing { public class EstimateSigningViewModel: EditEstimateViewModel { - public EstimateSigningViewModel(Estimate document) + public EstimateSigningViewModel(Estimate document): base(document) { - Data = document; } public Command ValidationCommand { get; set; } } diff --git a/BookAStar/BookAStar/ViewModels/Validation/EditingViewModel.cs b/BookAStar/BookAStar/ViewModels/Validation/EditingViewModel.cs new file mode 100644 index 00000000..36563df5 --- /dev/null +++ b/BookAStar/BookAStar/ViewModels/Validation/EditingViewModel.cs @@ -0,0 +1,83 @@ +using BookAStar.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; +using XLabs.Forms.Mvvm; +using System.ComponentModel; + +namespace BookAStar.ViewModels.Validation +{ + /// + /// Used to make the DataManager know how + /// to sync local and remote data + /// + public class EditingViewModel: ViewModel + { + + public Action CheckCommand { set; get; } + + public DataType Data { get; set; } + + private ModelState viewModelState = new ModelState(); + + public ModelState ViewModelState + { + get + { + return viewModelState; + } + set + { + base.SetProperty(ref viewModelState, value); + } + } + + public EditingViewModel(DataType data) + { + this.Data = data; + ViewModelState = new ModelState(); + } + + protected override void OnPropertyChanged(PropertyChangedEventArgs e) + { + base.OnPropertyChanged(e); + if (CheckCommand != null) + { + ViewModelState.Clear(); + CheckCommand(Data, ViewModelState); + } + } + + /* NOTE : I had a dream. + +bool existsRemotely; +public bool ExistsRemotely +{ + get + { + return existsRemotely; + } + set + { + base.SetProperty(ref existsRemotely, value); + } +} + +bool isDirty; +public bool IsDirty +{ + get + { + return isDirty; + } + set + { + base.SetProperty(ref isDirty, value); + } +} +*/ + } +} diff --git a/BookAStar/BookAStar/ViewModels/Validation/Error.cs b/BookAStar/BookAStar/ViewModels/Validation/Error.cs new file mode 100644 index 00000000..6642f105 --- /dev/null +++ b/BookAStar/BookAStar/ViewModels/Validation/Error.cs @@ -0,0 +1,13 @@ +namespace BookAStar.ViewModels.Validation +{ + public class InputError + { + public InputError(string errorMessage, ErrorSeverity severity) + { + Text = errorMessage; + Severity = severity; + } + public string Text { get; set; } + public ErrorSeverity Severity { get; set; } + } +} diff --git a/BookAStar/BookAStar/ViewModels/Validation/ErrorSeverity.cs b/BookAStar/BookAStar/ViewModels/Validation/ErrorSeverity.cs new file mode 100644 index 00000000..95dce487 --- /dev/null +++ b/BookAStar/BookAStar/ViewModels/Validation/ErrorSeverity.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BookAStar.ViewModels.Validation +{ + public enum ErrorSeverity + { + Crippling, + Bearable + } +} diff --git a/BookAStar/BookAStar/ViewModels/Validation/ModelState.cs b/BookAStar/BookAStar/ViewModels/Validation/ModelState.cs new file mode 100644 index 00000000..420911d4 --- /dev/null +++ b/BookAStar/BookAStar/ViewModels/Validation/ModelState.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xamarin.Forms; + +namespace BookAStar.ViewModels.Validation +{ + public class ModelState : BindableObject + { + public static readonly BindableProperty IsValidProperty = + BindableProperty.Create("IsValid", typeof(bool), typeof(ModelState), false); + + public static readonly BindableProperty ErrorsProperty = + BindableProperty.Create("Errors", typeof(Dictionary>), typeof(ModelState), null); + + public ModelState() + { + Errors = new Dictionary>(); + } + + public bool IsValid + { + get + { + return (bool) GetValue(IsValidProperty); + } + } + + public Dictionary> Errors + { + get + { + return (Dictionary>)GetValue(ErrorsProperty); + } + set + { + SetValue(ErrorsProperty, value); + } + } + + public virtual void AddError(string propertyName, string errorMessage, ErrorSeverity severity = ErrorSeverity.Crippling) + { + InputError e = new InputError(errorMessage, severity) ; + if (Errors.ContainsKey(propertyName)) + { + var errList = Errors[propertyName]; + errList.Add(e); + } + else + { + Errors.Add(propertyName, new List(new InputError [] { e })); + } + if (e.Severity < ErrorSeverity.Bearable) + SetValue(IsValidProperty, false); + } + + public virtual void Clear () + { + Errors.Clear(); + SetValue(IsValidProperty, true); + } + } +} diff --git a/YavscLib/YavscLib.nuget.targets b/YavscLib/YavscLib.nuget.targets new file mode 100644 index 00000000..85cf587b --- /dev/null +++ b/YavscLib/YavscLib.nuget.targets @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/YavscLib/project.json b/YavscLib/project.json index a6ce1f58..6204fd2a 100644 --- a/YavscLib/project.json +++ b/YavscLib/project.json @@ -14,6 +14,7 @@ }, "dependencies": {}, "frameworks": { - "net451": {} + "net451": {}, + ".NETPortable,Version=v4.5,Profile=Profile111": {} } } \ No newline at end of file diff --git a/YavscLib/project.lock.json b/YavscLib/project.lock.json index f701ebd4..a7ebab43 100644 --- a/YavscLib/project.lock.json +++ b/YavscLib/project.lock.json @@ -3,16 +3,14 @@ "version": 2, "targets": { ".NETFramework,Version=v4.5.1": {}, - ".NETPortable,Version=v4.5,Profile=Profile111": {}, - ".NETFramework,Version=v4.5.1/debian.8-x86": {}, - ".NETFramework,Version=v4.5.1/debian.8-x64": {}, - ".NETPortable,Version=v4.5,Profile=Profile111/debian.8-x86": {}, - ".NETPortable,Version=v4.5,Profile=Profile111/debian.8-x64": {} + ".NETPortable,Version=v4.5,Profile=Profile111": {} }, "libraries": {}, "projectFileDependencyGroups": { "": [], ".NETFramework,Version=v4.5.1": [], ".NETPortable,Version=v4.5,Profile=Profile111": [] - } + }, + "tools": {}, + "projectFileToolGroups": {} } \ No newline at end of file