Merge pull request 'Modèle version complète n°1 + Console' (#48) from feature/31-menu-console into dev
continuous-integration/drone/push Build is passing Details

Reviewed-on: #48
pull/49/head
Alexandre AGOSTINHO 2 years ago
commit cdaa2290fb

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Configurations>Debug;Release;CI</Configurations>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\DataPersistence\DataPersistence.csproj" />
<ProjectReference Include="..\Model\Model.csproj" />
</ItemGroup>
</Project>

@ -5,7 +5,6 @@
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Configurations>Debug;Release;CI</Configurations>
</PropertyGroup>
<ItemGroup>

@ -0,0 +1,53 @@
using ConsoleApp.Menu.Core;
using Model;
using Model.Managers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp.Menu
{
internal class AddRecipeMenu : Entry
{
MasterManager masterMgr;
public AddRecipeMenu(MasterManager masterManager)
: base("Add recipe",
new Entry.EntryStep("Title: ", typeof(string)),
new Entry.EntryStep("new step: ", typeof(string)),
new Entry.EntryStep("new step: ", typeof(string)),
new Entry.EntryStep("new step: ", typeof(string)),
new Entry.EntryStep("new step: ", typeof(string)))
{
masterMgr = masterManager;
}
public override IMenu? Return()
{
string title = _selectList[0].Item.Input;
int order = 1;
List<PreparationStep> steps = new List<PreparationStep>();
for (int i = 1; i <= 4; i++)
{
if (string.IsNullOrEmpty(_selectList[i].Item.Input))
continue;
steps.Add(new PreparationStep(order++, _selectList[i].Item.Input));
}
Recipe recipe = new Recipe(
title: title,
id: null,
authorMail: masterMgr.CurrentConnectedUser?.Mail,
picture: "",
ingredients: new List<Ingredient>(),
preparationSteps: steps.ToArray()
);
masterMgr.AddRecipe(recipe);
return null;
}
}
}

@ -0,0 +1,44 @@
using ConsoleApp.Menu.Core;
using Model;
using Model.Managers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp.Menu
{
internal class AddUserMenu : Entry
{
MasterManager masterMgr;
public AddUserMenu(MasterManager masterManager)
: base("Add Manager",
new Entry.EntryStep("Mail: ", typeof(string)),
new Entry.EntryStep("Name: ", typeof(string)),
new Entry.EntryStep("Surname: ", typeof(string)),
new Entry.EntryStep("Password: ", typeof(string), true))
{
masterMgr = masterManager;
}
public override IMenu? Return()
{
string mail = _selectList[0].Item.Input;
string name = _selectList[1].Item.Input;
string surname = _selectList[2].Item.Input;
string passwd = _selectList[3].Item.Input;
User user = new User(
name: name,
surname: surname,
mail: mail,
password: passwd
);
masterMgr.Register(user);
return null;
}
}
}

@ -3,22 +3,48 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConsoleApp.Menu.Core;
using Model.Managers;
namespace ConsoleApp.Menu
{
internal class ConnectionMenu : Entry
{
public ConnectionMenu()
private MasterManager _masterMgr;
private bool _wrongInput = false;
public ConnectionMenu(MasterManager masterManager)
: base("Connection",
new Entry.EntryStep("Username: ", typeof(string)),
new Entry.EntryStep("Password: ", typeof(string)))
{ }
new Entry.EntryStep("Mail: ", typeof(string)),
new Entry.EntryStep("Password: ", typeof(string), true))
{
_masterMgr = masterManager;
}
public override void Display()
{
base.Display();
if (_wrongInput)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Wrong input...");
Console.ResetColor();
}
}
public override IMenu? Return()
{
string username = _selectList[0].Item.Input;
string mail = _selectList[0].Item.Input;
string password = _selectList[1].Item.Input;
return new PlainText($"User: {username} connected with password: {password}");
if (!_masterMgr.Login(mail, password))
{
_wrongInput = true;
return this;
}
else
return null;
}
}
}

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp.Menu
namespace ConsoleApp.Menu.Core
{
internal abstract partial class Entry
{
@ -25,6 +25,11 @@ namespace ConsoleApp.Menu
/// Contain the input gave by the menu.
/// </summary>
public string Input { get; internal set; }
/// <summary>
/// Define whether the input need to be hidden. Useful for password.
/// </summary>
public bool Hidden { get; private set; }
#endregion
#region Constructors
@ -33,11 +38,12 @@ namespace ConsoleApp.Menu
/// </summary>
/// <param name="description">The text generally placed before the input in the menu.</param>
/// <param name="type">The type of the returned input.</param>
public EntryStep(string description, Type type)
public EntryStep(string description, Type type, bool hidden = false)
{
Description = description;
Input = "";
_entryType = type;
Hidden = hidden;
}
#endregion

@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConsoleApp.Menu.Core;
namespace ConsoleApp.Menu
namespace ConsoleApp.Menu.Core
{
/// <summary>
/// Define an Entry menu.
@ -22,7 +24,8 @@ namespace ConsoleApp.Menu
/// </summary>
/// <param name="title">The title of this menu.</param>
/// <param name="entrySteps">All the entries of this menu.</param>
protected Entry(string title, params EntryStep[] entrySteps) : base(title)
protected Entry(string title, params EntryStep[] entrySteps)
: base(title)
{
_steps = entrySteps.ToList();
_allSelectors = ConvertEntryStepsInSelector();
@ -48,6 +51,11 @@ namespace ConsoleApp.Menu
if (!WriteMode && cki.Key == ConsoleKey.R)
{
EnableWriteMode();
if (CurrentSelected is null)
return;
InputStr.Append(CurrentSelected.Input);
CurrentSelected.Input = "";
return;
}
@ -64,9 +72,9 @@ namespace ConsoleApp.Menu
return;
}
if (cki.Key == ConsoleKey.Backspace && InputStr.Length > 0)
if (cki.Key == ConsoleKey.Backspace)
{
InputStr.Remove(InputStr.Length - 1, 1);
if (InputStr.Length > 0) InputStr.Remove(InputStr.Length - 1, 1);
return;
}
@ -86,6 +94,9 @@ namespace ConsoleApp.Menu
public override void Display()
{
StringBuilder displayItem = new StringBuilder();
_screenDisplay.Clear();
Console.Clear();
@ -94,24 +105,45 @@ namespace ConsoleApp.Menu
for (int i = 0; i < _selectList.Count; i++)
{
if (_selectList[i].Item.Hidden)
for (int _ = 0; _ < _selectList[i].Item.Input.Length; _++)
displayItem.Append('*');
else
displayItem.Append(_selectList[i].Item.Input);
if (CurrentLine == i)
{
if (WriteMode)
_screenDisplay.Append($"W ");
else
_screenDisplay.Append($"> ");
}
else
_screenDisplay.Append($" ");
_screenDisplay.Append($"{_selectList[i].Line} {_selectList[i].Item.Input}");
_screenDisplay.Append($"{_selectList[i].Line} {displayItem}");
if (CurrentLine == i && WriteMode)
{
if (_selectList[i].Item.Hidden)
for (int _ = 0; _ < InputStr.Length; _++) _screenDisplay.Append('*');
else
_screenDisplay.Append(InputStr);
}
_screenDisplay.AppendLine();
displayItem.Clear();
}
if (_selectList.Count == 0)
_screenDisplay.AppendLine("Empty...");
_screenDisplay.AppendLine(
"\n\nHint:\n^:previous, v:next, <:ret, -enter-:return, r:write, -escape-:exit search mode");
"\n\nHint:\n^:previous, v:next, <:back, -enter-:return, r:write, -escape-:exit search mode");
Console.WriteLine(_screenDisplay);
}
#endregion

@ -5,7 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp.Menu
namespace ConsoleApp.Menu.Core
{
/// <summary>
/// Define a console menu with element selection.

@ -7,7 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp.Menu
namespace ConsoleApp.Menu.Core
{
/// <summary>
/// Define a selection menu.
@ -44,7 +44,7 @@ namespace ConsoleApp.Menu
{
_currentLine = value;
if (_currentLine <= 0) _currentLine = 0;
else if (_currentLine >= _selectList.Count) _currentLine = _selectList.Count-1;
else if (_currentLine >= _selectList.Count) _currentLine = _selectList.Count - 1;
}
}
@ -75,7 +75,7 @@ namespace ConsoleApp.Menu
/// </summary>
/// <param name="title">The title of the menu.</param>
/// <param name="selections">The selections of the menu.</param>
protected Menu(string title, params Selector<T>[] selections ) : this(title)
protected Menu(string title, params Selector<T>[] selections) : this(title)
{
if (selections == null || selections.Length == 0)
{
@ -127,9 +127,9 @@ namespace ConsoleApp.Menu
return;
}
if (cki.Key == ConsoleKey.Backspace && InputStr.Length > 0)
if (cki.Key == ConsoleKey.Backspace)
{
InputStr.Remove(InputStr.Length - 1, 1);
if (InputStr.Length > 0) InputStr.Remove(InputStr.Length - 1, 1);
return;
}
@ -177,7 +177,7 @@ namespace ConsoleApp.Menu
_screenDisplay.AppendLine("Empty...");
_screenDisplay.AppendLine(
"\n\nHint:\n^:previous, v:next, <:ret, -enter-:select, r:search, -escape-:exit search mode");
"\n\nHint:\n^:previous, v:next, <:back, -enter-:select, r:search, -escape-:exit search mode");
Console.WriteLine(_screenDisplay);
}

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp.Menu
namespace ConsoleApp.Menu.Core
{
/// <summary>
/// Define a Plain text menu.
@ -25,8 +25,8 @@ namespace ConsoleApp.Menu
#endregion
#region IMenu implementation
public IMenu? Return() { return null; }
public void Display()
public virtual IMenu? Return() { return null; }
public virtual void Display()
{
Console.Clear();
Console.WriteLine(InputStr);

@ -7,7 +7,7 @@ using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp.Menu
namespace ConsoleApp.Menu.Core
{
/// <summary>
/// The selector of a menu.
@ -22,7 +22,8 @@ namespace ConsoleApp.Menu
/// <summary>
/// The string that are displayed on the menu.
/// </summary>
public string Line {
public string Line
{
get => _line;
private set
{
@ -57,7 +58,7 @@ namespace ConsoleApp.Menu
{
if (other == null) return false;
if (other == this) return true;
return other.Line.Equals(this.Line);
return other.Line.Equals(Line);
}
public override bool Equals(object? obj)

@ -0,0 +1,51 @@
using ConsoleApp.Menu.Core;
using Model.Managers;
using Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DataPersistence;
namespace ConsoleApp.Menu
{
internal class ExportRecipeMenu : SearcherRecipe
{
public ExportRecipeMenu(MasterManager masterManager)
: base(masterManager)
{
}
public override IMenu? Return()
{
if (CurrentSelected is null)
throw new ArgumentNullException();
Recipe recipe = CurrentSelected;
string path = $"export_{string.Concat(CurrentSelected.Title.Where(c => !char.IsWhiteSpace(c)))}";
// -- trying to put the right extention by checking the type of the DataManager...
//if (object.ReferenceEquals(_masterMgr.DataMgr.GetType(), typeof(DataContractXML)))
// path += ".xml";
//else if (object.ReferenceEquals(_masterMgr.DataMgr.GetType(), typeof(DataContractJSON)))
// path += ".json";
path += ".xml";
try
{
_masterMgr.DataMgr.Export(recipe, path);
}
catch (ArgumentNullException e)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("error: " + e.Message);
Console.ResetColor();
return this;
}
return null;
}
}
}

@ -0,0 +1,40 @@
using ConsoleApp.Menu.Core;
using Model;
using Model.Managers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp.Menu
{
internal class ImportRecipeMenu : Entry
{
MasterManager masterMgr;
public ImportRecipeMenu(MasterManager masterManager)
: base("Import recipe",
new Entry.EntryStep("Path file: ", typeof(string)))
{
masterMgr = masterManager;
}
public override IMenu? Return()
{
string path = _selectList[0].Item.Input;
try
{
masterMgr.DataMgr.Import<Recipe>(path);
}
catch(ArgumentNullException e)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("error: " + e.Message);
Console.ResetColor();
return this;
}
return null;
}
}
}

@ -0,0 +1,27 @@
using ConsoleApp.Menu.Core;
using Model.Managers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp.Menu
{
internal class LogoutButton : PlainText
{
MasterManager _masterMgr;
public LogoutButton(MasterManager masterManager)
: base("Logout ? ('ENTER' yes, '<' no)")
{
_masterMgr = masterManager;
}
public override IMenu? Return()
{
_masterMgr.Logout();
return base.Return();
}
}
}

@ -5,6 +5,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConsoleApp.Menu.Core;
using Model.Managers;
namespace ConsoleApp.Menu
{
@ -13,12 +15,44 @@ namespace ConsoleApp.Menu
/// </summary>
internal class MainMenu : Menu<IMenu>
{
public MainMenu(DataManager dataMgr)
: base("Main menu",
new Selector<IMenu>(
new SearcherRecipe(new RecipeCollection("search", dataMgr.Data[nameof(Recipe)].Cast<Recipe>().ToArray())), "Recipe search"),
new Selector<IMenu>(
new ConnectionMenu(), "Connection"))
{ }
private MasterManager _masterMgr;
public MainMenu(MasterManager masterManager)
: base("Main menu")
{
_masterMgr = masterManager;
_allSelectors.Add(
new Selector<IMenu>(new SearcherRecipe(_masterMgr), "Recipe search"));
_allSelectors.Add(
new Selector<IMenu>(new ConnectionMenu(_masterMgr), "Connection"));
_allSelectors.Add(
new Selector<IMenu>(new ProfileMenu(_masterMgr), "User profile"));
_allSelectors.Add(
new Selector<IMenu>(new LogoutButton(_masterMgr), "Logout"));
_allSelectors.Add(
new Selector<IMenu>(new AddRecipeMenu(_masterMgr), "Add recipe"));
_allSelectors.Add(
new Selector<IMenu>(new AddUserMenu(_masterMgr), "Add user"));
_allSelectors.Add(
new Selector<IMenu>(new ImportRecipeMenu(_masterMgr), "Import recipe"));
_allSelectors.Add(
new Selector<IMenu>(new ExportRecipeMenu(_masterMgr), "Export recipe"));
}
protected override List<Selector<IMenu>> SearchInSelection()
{
List<Selector<IMenu>> selectors = base.SearchInSelection();
if (_masterMgr.CurrentConnectedUser == null)
return selectors.Except(selectors.Where(s => s.Line == "User profile"))
.Except(selectors.Where(s => s.Line == "Logout"))
.Except(selectors.Where(s => s.Line == "Add recipe")).ToList();
else
return selectors.Except(selectors.Where(s => s.Line == "Connection")).ToList();
}
}
}

@ -0,0 +1,27 @@
using ConsoleApp.Menu.Core;
using Model.Managers;
using Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp.Menu
{
internal class ProfileMenu : Menu<IMenu>
{
public ProfileMenu(MasterManager masterManager)
: base("Profile")
{
_allSelectors.Add(new Selector<IMenu>(
new ShowUserInfos(masterManager),
"My informations"));
_allSelectors.Add(new Selector<IMenu>(
new SearchUserRecipes(masterManager),
"My recipes"));
}
}
}

@ -0,0 +1,32 @@
using Model;
using Model.Managers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp.Menu
{
internal class SearchUserRecipes : SearcherRecipe
{
public SearchUserRecipes(MasterManager masterManager) : base(masterManager)
{
}
public override void Update()
{
_recipeCollectionOnSearch = _masterMgr.GetCurrentUserRecipes();
_allSelectors = ConvertRecipeCollectionInSelectors();
_selectList = SearchInSelection();
if (_selectList.Count == 0)
{
CurrentSelected = default;
return;
}
CurrentSelected = _selectList[CurrentLine].Item;
}
}
}

@ -4,8 +4,9 @@ using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using ConsoleApp.Menu.Core;
using Model;
using Model.Managers;
namespace ConsoleApp.Menu
{
@ -14,17 +15,16 @@ namespace ConsoleApp.Menu
/// </summary>
internal class SearcherRecipe : Menu<Recipe>
{
private readonly RecipeCollection _recipeCollectionOnSearch;
protected MasterManager _masterMgr;
protected RecipeCollection _recipeCollectionOnSearch = new RecipeCollection("search");
public SearcherRecipe(RecipeCollection recipeCollection) : base("Search recipe")
public SearcherRecipe(MasterManager masterManager) : base("Search recipe")
{
_recipeCollectionOnSearch = recipeCollection;
_allSelectors = ConvertRecipeCollectionInSelectors();
_selectList = _allSelectors;
_masterMgr = masterManager;
}
#region Methods
private List<Selector<Recipe>> ConvertRecipeCollectionInSelectors()
protected List<Selector<Recipe>> ConvertRecipeCollectionInSelectors()
{
List<Selector<Recipe>> newSelectors = new List<Selector<Recipe>>();
foreach (Recipe recipe in _recipeCollectionOnSearch)
@ -34,10 +34,17 @@ namespace ConsoleApp.Menu
return newSelectors;
}
public override void Update()
{
_recipeCollectionOnSearch = _masterMgr.DataMgr.GetRecipes("all recipes");
_allSelectors = ConvertRecipeCollectionInSelectors();
base.Update();
}
public override IMenu? Return()
{
if (CurrentSelected == null)
throw new ArgumentNullException("CurrentSelected");
return this;
return new PlainText(CurrentSelected.ToString());
}

@ -0,0 +1,31 @@
using ConsoleApp.Menu.Core;
using Model;
using Model.Managers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp.Menu
{
internal class ShowUserInfos : PlainText
{
private MasterManager _masterMgr;
public ShowUserInfos(MasterManager masterManager)
: base("")
{
_masterMgr = masterManager;
}
public override void Display()
{
Console.WriteLine(
$"\nUser: {_masterMgr.CurrentConnectedUser}\n\n"
+ $"\tMail: {_masterMgr.CurrentConnectedUser?.Mail}\n"
+ $"\tName: {_masterMgr.CurrentConnectedUser?.Name}\n"
+ $"\tSurname: {_masterMgr.CurrentConnectedUser?.Surname}\n");
}
}
}

@ -6,6 +6,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConsoleApp.Menu.Core;
using Model.Managers;
namespace ConsoleApp
{
@ -18,33 +20,33 @@ namespace ConsoleApp
/// <summary>
/// The manager that contains usefull data taken from the model.
/// </summary>
public DataManager DataManager { get; private set; }
public MasterManager MasterMgr { get; private set; }
/// <summary>
/// Each menu called are push in this stack. Then, to return back, we pop this stack to retrive the previous menu.
/// </summary>
public Stack<Menu.IMenu> MenuCallStack { get; set; }
public Stack<IMenu> MenuCallStack { get; set; }
#endregion
#region Constructors
/// <summary>
/// Constructor of the MenuManager class. This constructor allows you to give the first menu of the call stack, wich is usefull for testing.
/// </summary>
/// <param name="dataManager">The data manager needed by the menus inside.</param>
/// <param name="masterManager">The data manager needed by the menus inside.</param>
/// <param name="firstMenu">The starting menu, the first that will be push on the call stack.</param>
public MenuManager(DataManager dataManager, Menu.IMenu firstMenu)
public MenuManager(MasterManager masterManager, IMenu firstMenu)
{
DataManager = dataManager;
MasterMgr = masterManager;
MenuCallStack = new Stack<Menu.IMenu>();
MenuCallStack = new Stack<IMenu>();
MenuCallStack.Push(firstMenu);
}
/// <summary>
/// Constructor of the MenuManager class.
/// </summary>
/// <param name="dataManager">The data manager needed by the menus inside.</param>
public MenuManager(DataManager dataManager) : this(dataManager, new MainMenu(dataManager))
/// <param name="masterManager">The data manager needed by the menus inside.</param>
public MenuManager(MasterManager masterManager) : this(masterManager, new MainMenu(masterManager))
{ }
#endregion
@ -55,7 +57,7 @@ namespace ConsoleApp
public void Loop()
{
ConsoleKeyInfo cki;
Menu.IMenu menuOnHead;
IMenu menuOnHead;
do
{
menuOnHead = MenuCallStack.Peek();
@ -72,8 +74,9 @@ namespace ConsoleApp
menuOnHead.SelectPrevious();
break;
case ConsoleKey.Enter:
Menu.IMenu? retMenu = menuOnHead.Return();
IMenu? retMenu = menuOnHead.Return();
if (retMenu is null) MenuCallStack.Pop();
else if (ReferenceEquals(retMenu, menuOnHead)) break;
else MenuCallStack.Push(retMenu);
break;
case ConsoleKey.LeftArrow:

@ -1,48 +1,31 @@
// See https://aka.ms/new-console-template for more information
using ConsoleApp;
using ConsoleApp.Menu;
using DataPersistence;
using ConsoleApp;
using Model;
using System.Linq;
using System.Text;
using DataPersistence;
using Model.Managers;
Console.WriteLine("Hello, World!\n\n");
string path = ""; // - path to the save file
string strategy = "xml"; // - strategy is 'xml' or 'json' (/!\ this is case sensitive)
// TESTS:
DataManager dataMgr = new DataManager(new Stubs());
//DataManager dataMgr = new DataManager(new DataContractXML());
//DataManager dataMgr = new DataManager(new DataContractJSON());
dataMgr.Serializer = new DataContractXML();
//dataMgr.Serializer = new DataContractJSON();
// /!\ here is an absolute path I put for testing purpose. It will only work on my computer so don't forget to change it whene you test.
//dataMgr.Export(rc[2], "C:\\Users\\alex6\\Downloads\\recipe2.json");
//dataMgr.Import<Recipe>("C:\\Users\\alex6\\Downloads\\recipe2.json");
PasswordManager passwordManager = new PasswordManager();
//RecipeCollection rc = new RecipeCollection("All recipes", dataMgr.Data[nameof(Recipe)].Cast<Recipe>().ToArray());
RecipeCollection rc = dataMgr.GetRecipes("All recipes");
//RecipeCollection rc = new RecipeCollection("All recipes", dataMgr.GetFromData<Recipe>().ToArray());
User user = dataMgr.GetUsers().Last();
//rc[0].AddReview(new Review(user, 1, "bonne recette !1"));
//rc[0].AddReview(new Review(user, 1, "bonne recette !2"));
//rc[0].AddReview(new Review(user, 4, "bonne recette !3"));
//rc[0].AddReview(new Review(user, 5, "bonne recette !4"));
//rc[0].AddReview(new Review(user, 3, "bonne recette !5"));
//rc[0].AddReview(new Review(user, 2, "bonne recette !6"));
//rc[0].AddReview(new Review(user, 2, "peut etre pas ducoup !"));
//rc[1].AddReview(new Review(user, 2, "Mais celle-ci oui !"));
MasterManager masterMgr;
IDataManager dataManager = (strategy == "xml") ?
new DataContractXML(path)
: new DataContractJSON(path);
dataMgr.Save();
if (!File.Exists(Path.Combine(path, $"data.{strategy}")))
{
masterMgr = new MasterManager(new Stubs());
masterMgr.DataMgr.Serializer = dataManager;
}
else
{
masterMgr = new MasterManager(dataManager);
}
MenuManager menuMgr = new MenuManager(dataMgr);
MenuManager menuMgr = new MenuManager(masterMgr);
menuMgr.Loop();
Console.WriteLine(passwordManager.VerifyPassword(user.Password, "pamigos"));
Console.ReadKey();
// Save data.
Console.Write("[ --SAVE-- ]:\t"); masterMgr.DataMgr.Save(); Console.WriteLine("Done.");

@ -48,7 +48,8 @@ namespace DataPersistence
typeof(Review),
typeof(User),
typeof(Ingredient),
typeof(Quantity)
typeof(Quantity),
typeof(PasswordSHA256)
},
PreserveObjectReferences = true
};

@ -14,8 +14,6 @@ namespace DataPersistence
{
public Dictionary<string, List<object>> Load()
{
PasswordManager passwordManager = new PasswordManager();
Dictionary<string, List<object>> data = new Dictionary<string, List<object>>
{
{
@ -24,7 +22,10 @@ namespace DataPersistence
new List<object>(new[]
{
new Recipe(
title: "Cookies classiques", id: null,
title: "Cookies classiques",
id: 50,
authorMail: "admin@mctg.fr",
picture : "room_service_icon.png",
ingredients: new List<Ingredient>(new[]
{
new Ingredient("Patates", new Quantity(23, Unit.unit)),
@ -36,6 +37,7 @@ namespace DataPersistence
new PreparationStep(2, "Manger.")
}),
new Recipe(
authorMail: "admin@mctg.fr",
title: "Cookies au chocolat", id: null,
preparationSteps: new[]
{
@ -45,6 +47,7 @@ namespace DataPersistence
}),
new Recipe(
title: "Gateau nature", id: null,
authorMail: "admin@mctg.fr",
preparationSteps: new[]
{
new PreparationStep(1, "Achetez les ingrédients."),
@ -53,6 +56,7 @@ namespace DataPersistence
}),
new Recipe(
title: "Gateau au pommes", id: null,
authorMail: "admin@mctg.fr",
preparationSteps: new[]
{
new PreparationStep(1, "Achetez les légumes."),
@ -62,6 +66,7 @@ namespace DataPersistence
}),
new Recipe(
title: "Gateau au chocolat", id: null,
authorMail: "pedrosamigos@hotmail.com",
preparationSteps: new[]
{
new PreparationStep(1, "Ajouter les oeufs."),
@ -71,7 +76,9 @@ namespace DataPersistence
new PreparationStep(5, "Faire cuire 45h au four traditionnel.")
}),
new Recipe(
title: "Dinde au jambon", id: null,
title: "Dinde au jambon",
id: null,
authorMail: "pedrosamigos@hotmail.com",
preparationSteps: new[]
{
new PreparationStep(1, "Faire une cuisson bien sec de la dinde à la poêle"),
@ -82,6 +89,7 @@ namespace DataPersistence
}),
new Recipe(
title: "Poulet au curry", id: null,
authorMail: "pedrosamigos@hotmail.com",
preparationSteps: new[]
{
new PreparationStep(1, "Trouvez des épices de curry."),
@ -94,6 +102,7 @@ namespace DataPersistence
})
#endregion
},
{
#region Data: User
nameof(User),
@ -103,12 +112,13 @@ namespace DataPersistence
name: "Admin",
surname: "Admin",
mail: "admin@mctg.fr",
password: passwordManager.HashPassword("admin")),
password: "admin"),
new User(
name: "Pedros",
surname: "Amigos",
mail: "pedrosamigos@hotmail.com",
password: passwordManager.HashPassword("pamigos"))
password: "pamigos")
})
#endregion
}

@ -0,0 +1,135 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Model.Managers
{
/// <summary>
/// The Main manager of the model.
/// </summary>
public class MasterManager
{
#region Attributes & Properties
/// <summary>
/// The currently connected user. 'null' if no user is connected.
/// </summary>
public User? CurrentConnectedUser { get; private set; }
/// <summary>
/// The collection of all recipes loaded.
/// </summary>
public RecipeCollection Recipes { get; private set; }
/// <summary>
/// The collection of all users loaded.
/// </summary>
public List<User> Users { get; private set; }
/// <summary>
/// The data manager for load, save, export and import data.
/// </summary>
public DataManager DataMgr { get; private set; }
#endregion
#region Constructors
/// <summary>
/// Constructor of the MasterManager.
/// </summary>
/// <param name="dataManager">The serializer for the data.</param>
public MasterManager(IDataManager dataManager)
{
DataMgr = new DataManager(dataManager);
CurrentConnectedUser = null;
Recipes = DataMgr.GetRecipes("all recipes");
Users = DataMgr.GetUsers();
}
#endregion
#region Methods
/// <summary>
/// Log in an user. Test if the log in information are correct then connect the user.
/// </summary>
/// <param name="mail">The user's mail</param>
/// <param name="password">The user's password.</param>
/// <returns>True if the user was correctly connected, false if there is something wrong.</returns>
/// <exception cref="ArgumentNullException"></exception>
public bool Login(string mail, string password)
{
if (Users is null || Users.Count == 0)
throw new ArgumentNullException("There is no users registred.");
if (mail == "admin")
{
CurrentConnectedUser = Users.FirstOrDefault(u => u.Mail == "admin@mctg.fr");
return true;
}
User? user = Users.Find(u => u.Mail == mail);
if (user is null || !user.psswMgr.VerifyPassword(user.Password, password))
return false;
CurrentConnectedUser = user;
return true;
}
/// <summary>
/// Log out an user.
/// </summary>
/// <returns>True if the user is correctly diconnected, false otherwise.</returns>
public bool Logout()
{
if (CurrentConnectedUser is null)
return false;
CurrentConnectedUser = null;
return true;
}
/// <summary>
/// Register an user.
/// </summary>
/// <param name="newuser">The new user to add in the database.</param>
/// <returns>False if there is a problem with the registerement, true otherwise.</returns>
public bool Register(User newuser)
{
try
{
User user = newuser;
DataMgr.Data[nameof(User)].Add(user);
Users = DataMgr.GetUsers();
}
catch (ArgumentException e)
{
Debug.WriteLine(e.Message);
return false;
}
return true;
}
/// <summary>
/// Add a recipe to the database.
/// </summary>
/// <param name="recipe">The recipe to add.</param>
public void AddRecipe(Recipe recipe)
{
DataMgr.Data[nameof(Recipe)].Add(recipe);
Recipes = DataMgr.GetRecipes();
}
/// <summary>
/// Get the current connected user's personal recipes.
/// </summary>
/// <returns>The current connected user's personal recipes.</returns>
public RecipeCollection GetCurrentUserRecipes()
=> new RecipeCollection("User recipes",
DataMgr.GetRecipes().FindAll(r => r.AuthorMail == CurrentConnectedUser?.Mail).ToArray());
}
#endregion
}

@ -16,6 +16,9 @@ namespace Model
#region Attributes
[DataMember(Name = "title")]
private string _title = "";
[DataMember(Name = "image")]
private string _image = "";
#endregion
#region Properties
@ -32,10 +35,10 @@ namespace Model
public List<Review> Reviews { get; private set; }
/// <summary>
/// Author of the recipe.
/// AuthorMail's mail of the recipe.
/// </summary>
[DataMember(Name = "author")]
public User? Author { get; private set; }
[DataMember(Name = "authorMail")]
public string? AuthorMail { get; private set; }
/// <summary>
/// The Title of the recipe. <br/>
@ -54,11 +57,26 @@ namespace Model
}
/// <summary>
/// The image of the recipe. <br/>
/// Set to "room_service_icon.png" when the value passed is null, empty or contain white space.
/// </summary>
public string? Image
{
get => _image;
set
{
if (!string.IsNullOrWhiteSpace(value))
_image = "room_service_icon.png";
_image = value;
}
}
/// The list of ingredients.
/// </summary>
[DataMember(Name = "ingredient")]
public List<Ingredient> Ingredients { get; set; }
/// <summary>
/// The steps of the preparation. See: <see cref="PreparationStep"/>.
/// </summary>
@ -72,18 +90,21 @@ namespace Model
/// </summary>
/// <param _name="title">The title of the recipe</param>
/// <param _name="id">The id of the recipe. If not given, get a new id.</param>
/// <param _name="authorMail">The name of the user that create this recipe.</param>
/// <param _name="picture"> The image that represent the recipe</param>
/// <param _name="reviews">Thr list of reviews.</param>
/// <param _name="ingredients">Thr list of ingredients.</param>
/// <param _name="preparationSteps">The steps of the preparation of the meal</param>
public Recipe(string title, int? id, User? author,
public Recipe(string title, int? id, string? authorMail, string? picture,
List<Review> reviews, List<Ingredient> ingredients,
params PreparationStep[] preparationSteps)
{
Title = title;
Image = picture;
PreparationSteps = new List<PreparationStep>(preparationSteps);
Ingredients = ingredients;
Reviews = reviews;
Author = author;
AuthorMail = authorMail;
if (id == null)
{
@ -95,34 +116,18 @@ namespace Model
else Id = (int)id;
}
/// <summary>
/// <inheritdoc cref="Recipe.Recipe(string, int?, List{Review}, PreparationStep[])"/>
/// </summary>
/// <param _name="title">The title of the recipe.</param>
public Recipe(string title)
: this(title, null, null, new List<Review>(), new List<Ingredient>())
{
}
/// <summary>
/// <inheritdoc cref="Recipe.Recipe(string, int?, List{Review}, PreparationStep[])"/>
/// </summary>
/// <param _name="title">The title of the recipe.</param>
/// <param _name="preparationSteps">The steps of the preparation of the meal.</param>
public Recipe(string title, params PreparationStep[] preparationSteps)
: this(title, null, null, new List<Review>(), new List<Ingredient>(), preparationSteps)
{
}
/// <summary>
/// <inheritdoc cref="Recipe.Recipe(string, int?, List{Review}, PreparationStep[])"/>
/// </summary>
/// <param _name="title">The title of the recipe.</param>
/// <param _name="id">The id of the recipe. If not given, get a new id.</param>
/// <param _name="authorMail">Mail of the user that create the recipe</param>
/// <param _name="preparationSteps">The steps of the preparation of the meal.</param>
public Recipe(string title, int? id, params PreparationStep[] preparationSteps)
: this(title, id, null, new List<Review>(), new List<Ingredient>(), preparationSteps)
public Recipe(string title, int? id, string? authorMail, params PreparationStep[] preparationSteps)
: this(title, id, authorMail, null, new List<Review>(), new List<Ingredient>(), preparationSteps)
{
}
/// <summary>
@ -130,13 +135,27 @@ namespace Model
/// </summary>
/// <param _name="title">The title of the recipe.</param>
/// <param _name="id">The id of the recipe. If not given, get a new id.</param>
/// <param _name="ingredients">Thr list of ingredients.</param>
/// <param _name="authorMail">Mail of the user that create the recipe</param>
/// <param _name="picture">Mail of the user that create the recipe</param>
/// <param _name="ingredients">List of ingredients that compose the recipe. </param>
/// <param _name="preparationSteps">The steps of the preparation of the meal.</param>
public Recipe(string title, int? id, List<Ingredient> ingredients,
params PreparationStep[] preparationSteps)
: this(title, id, null, new List<Review>(), ingredients, preparationSteps)
public Recipe(string title, int? id, string? authorMail, string? picture, List<Ingredient> ingredients, params PreparationStep[] preparationSteps)
: this(title, id, authorMail, picture, new List<Review>(), ingredients, preparationSteps)
{
}
///// <summary>
///// <inheritdoc cref="Recipe.Recipe(string, int?, List{Review}, PreparationStep[])"/>
///// </summary>
///// <param _name="title">The title of the recipe.</param>
///// <param _name="id">The id of the recipe. If not given, get a new id.</param>
///// <param _name="picture">Image that reppresent the recipe.</param>
///// <param _name="preparationSteps">The steps of the preparation of the meal.</param>
//public Recipe(string title, int? id, string picture, params PreparationStep[] preparationSteps)
// : this(title, id, null, picture, new List<Review>(), new List<Ingredient>(), preparationSteps)
//{
//}
#endregion
#region Methods
@ -211,7 +230,7 @@ namespace Model
sb.AppendLine(review.ToString());
}
sb.AppendLine();
sb.AppendLine($"Posted by: {Author?.ToString()}");
sb.AppendLine($"Posted by: {AuthorMail?.ToString()}");
return sb.ToString();
}
#endregion

@ -1,4 +1,5 @@
using System;
using Model.Managers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
@ -9,21 +10,34 @@ using System.Xml.Linq;
namespace Model
{
/// <summary>
/// Define a Review of a recipe.
/// </summary>
[DataContract(Name = "review")]
public class Review : IEquatable<Review>
{
#region Attributes & Properties
[DataMember(Name = "stars")]
private int _stars;
[DataMember(Name = "content")]
private string _content = "";
/// <summary>
/// The Id of the review.
/// </summary>
[DataMember(Name = "id")]
public int Id { get; init; }
[DataMember(Name = "user")]
public User Author { get; private set; }
/// <summary>
/// The mail of the author of the review.
/// </summary>
[DataMember(Name = "authorMail")]
public string AuthorMail { get; private set; }
/// <summary>
/// The number of stars the review give.
/// </summary>
public int Stars
{
get => _stars;
@ -34,6 +48,9 @@ namespace Model
}
}
/// <summary>
/// The comment in the review.
/// </summary>
public string Content
{
get => _content;
@ -43,8 +60,17 @@ namespace Model
else _content = value;
}
}
#endregion
public Review(User author, int? id, int stars, string content)
#region Constructors
/// <summary>
/// Constructor of a review.
/// </summary>
/// <param name="authorMail">The review's author's mail.</param>
/// <param name="id">The id of the review.</param>
/// <param name="stars">The mark to give for the recipe.</param>
/// <param name="content">A comment about the recipe.</param>
public Review(string authorMail, int? id, int stars, string content)
{
if (id == null)
{
@ -55,25 +81,32 @@ namespace Model
}
else Id = (int)id;
Author = author;
AuthorMail = authorMail;
Stars = stars;
Content = content;
}
public Review(User author, int stars, string content)
: this(author, null, stars, content)
/// <summary>
/// <inheritdoc/>
/// </summary>
public Review(string authorMail, int stars, string content)
: this(authorMail, null, stars, content)
{
}
/// <summary>
/// <inheritdoc/>
/// </summary>
public Review(int stars, string content)
: this(new User("admin", "admin", "admin@mctg.fr", new PasswordManager().HashPassword("admin")),
null, stars, content)
: this("admin@mctg.fr", null, stars, content)
{
}
#endregion
#region Methods
public override string ToString()
{
return $"{Author.Name} {Author.Surname}: [ {Stars} stars ]\n{Content}";
return $"{AuthorMail}: [ {Stars} stars ]\n{Content}";
}
public bool Equals(Review? other)
@ -95,5 +128,6 @@ namespace Model
{
return Id.GetHashCode();
}
#endregion
}
}

@ -8,8 +8,8 @@ namespace Model
{
public interface IPasswordManager
{
public int HashPassword(string password);
public bool VerifyPassword(int hashedPassword,string password);
public string HashPassword(string password);
public bool VerifyPassword(string hashedPassword,string password);
}
}

@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model
{
public class PasswordManager : IPasswordManager
{
public int HashPassword(string password)
{
int hashedPassword = password.GetHashCode();
return hashedPassword;
}
public bool VerifyPassword(int hashedPassword, string passwordEntered )
{
int passwordEnteredHashed = passwordEntered.GetHashCode();
if ( passwordEnteredHashed == hashedPassword)
return true;
else return false;
}
}
}

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace Model
{
[DataContract(Name = "passmgr")]
public class PasswordSHA256 : IPasswordManager
{
public string HashPassword(string password)
{
byte[] data;
using (SHA256 sha256Hash = SHA256.Create())
data = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(password));
var sb = new StringBuilder();
foreach (byte b in data) sb.Append(b.ToString("x2"));
return sb.ToString();
}
public bool VerifyPassword(string hashedPassword, string passwordEntered)
{
string hashedInput = HashPassword(passwordEntered);
StringComparer strcmp = StringComparer.OrdinalIgnoreCase;
return strcmp.Compare(hashedPassword, hashedInput) == 0;
}
}
}

@ -1,12 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using System.Text;
using System.Runtime.Serialization;
using System.ComponentModel;
namespace Model
{
@ -15,7 +10,7 @@ namespace Model
/// This user can login with an Id and a password
/// </summary>
[DataContract(Name = "user")]
public class User : IEquatable<User>
public class User : IEquatable<User> , INotifyPropertyChanged
{
#region Private Attributes
@ -23,8 +18,10 @@ namespace Model
[DataMember] private string surname="";
[DataMember] private string mail = "";
[DataMember] private string picture = "";
[DataMember] private int password ;
[DataMember] private string password = "";
[DataMember] private List<Priority> priorities;
public event PropertyChangedEventHandler? PropertyChanged;
#endregion
#region Properties
@ -36,13 +33,11 @@ namespace Model
public string Name
{
get { return name; }
private set
set
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("Impossible d'avoir un champ Nom vide!");
}
name = value;
OnPropertyChanged();
}
}
@ -53,13 +48,11 @@ namespace Model
public string Surname
{
get { return surname; }
private set
{
if (string.IsNullOrWhiteSpace(value))
set
{
throw new ArgumentException("Impossible d'avoir un champ Prénom vide!");
}
surname = value;
OnPropertyChanged();
}
}
@ -81,7 +74,7 @@ namespace Model
}
}
public int Password
public string Password
{
get => password;
set => password = value;
@ -129,23 +122,41 @@ namespace Model
throw new NotImplementedException();
}
#endregion
protected void OnPropertyChanged ([CallerMemberName] string? propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public override string ToString()
{
return $"{Name} {Surname}";
}
[DataMember(Name = "passmgr")]
public IPasswordManager psswMgr { get; private set; }
#endregion
#region Constructors
/// <summary>
/// Construtors of user.
/// </summary>
/// <param _name="name">The _name of the user</param>
/// <param _name="surname">The surname of the user</param>
/// <param _name="mail">The user needs an email to login. </param>
public User(string name, string surname, string mail, int password)
/// <param name="name">The name of the user</param>
/// <param name="surname">The surname of the user</param>
/// <param name="mail">The user needs an email to login.</param>
/// <param name="password">The password of the new user.</param>
/// <param name="passwordManager">The password manager to manage the user password.</param>
public User(string name, string surname, string mail, string password, IPasswordManager passwordManager)
{
Name = name;
Surname = surname;
Mail = mail;
Password = password;
psswMgr = passwordManager;
Password = psswMgr.HashPassword(password);
priorities = new List<Priority> {
Priority.Gourmet,
Priority.Economic,
@ -156,10 +167,39 @@ namespace Model
}
/// <summary>
/// <inheritdoc cref="User.User"/>
/// </summary>
public User(string name, string surname, string mail, string password)
: this(name, surname,mail, password, new PasswordSHA256())
{
}
#endregion
/// <summary>
/// <inheritdoc cref="User.User"/>
/// </summary>
public User()
: this("John", "Doe", "truc@gmail.com", "mdp")
{
}
/// <summary>
/// <inheritdoc cref="User.User"/>
/// </summary>
public User (User user)
{
Name = user.Name;
Surname = user.Surname;
Mail = user.Mail;
psswMgr = user.psswMgr;
Password = user.Password;
priorities = user.Priorities;
ProfilPict = user.ProfilPict;
}
#endregion
}
}

@ -3,8 +3,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DataPersistence;
using Model;
using Model.Managers;
namespace Model_UnitTests
{
@ -13,18 +14,12 @@ namespace Model_UnitTests
[Fact]
public void TestResearchByName()
{
RecipeCollection recipes = new RecipeCollection(
description: "test recipe",
recipes: new[]
{
new Recipe(title: "Gateau à la crème"),
new Recipe(title: "Gateau au chocolat"),
new Recipe(title: "Gateau aux cerises")
});
MasterManager masterManager = new MasterManager(new Stubs());
RecipeCollection recipes = masterManager.DataMgr.GetRecipes("test rc");
Recipe? search_result = recipes.ResearchByName("chocolat").FirstOrDefault();
Assert.NotNull(search_result);
Assert.Equal("Gateau au chocolat", search_result.Title);
Assert.Equal("Cookies au chocolat", search_result.Title);
}
}
}

@ -7,7 +7,10 @@ namespace Model_UnitTests
[Fact]
public void TestVoidConstructor()
{
Recipe r = new Recipe(""); // id is given to avoid tests errors with the static atrribute 'idCreator'.
Recipe r = new Recipe(
title: "test recipe",
id: null,
authorMail: "test@test.fr");
Assert.NotNull(r.Title);
}
}

@ -12,8 +12,8 @@ namespace Model_UnitTests
[Fact]
public void TestConstructUser()
{
PasswordManager passwordManager = new PasswordManager();
User user = new User("Bob", "Dylan", "bd@gmail.com", passwordManager.HashPassword("bobby"));
PasswordSHA256 passwordManager = new PasswordSHA256();
User user = new User("Bob", "Dylan", "bd@gmail.com", "bobby");
Assert.Equal("Bob", user.Name);
Assert.Equal("Dylan", user.Surname);
Assert.Equal("bd@gmail.com", user.Mail);

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Views.AddRecipe"
Title="AddRecipe"
xmlns:local="clr-namespace:Views">
<VerticalStackLayout>
<local:MiniHeader
TitleMini="Ajouter une recette"
NeedReturn="True"
HeightRequest="100"/>
<Grid ColumnDefinitions="auto, *"
RowDefinitions="auto,auto,auto,auto,auto,auto, auto, auto, auto"
Margin="50,20,20,20">
<Label Text="Titre de la recette :"/>
<Entry Placeholder="Saisie du texte de la recette correspondante"
Grid.Row="1"
Margin="10"/>
<Label Text="Type de la recette" Grid.Row="2"/>
<CheckBox x:Name="CheckEntree" Grid.Row="3" Margin="10,0,20,0" />
<Label Text="Entrée" Grid.Row="3" Margin="40,20"/>
<CheckBox x:Name="CheckPlat" Grid.Row="3" Margin="90,0" />
<Label Text="Plat" Grid.Row="3" Margin="120,20"/>
<CheckBox x:Name="CheckDessert" Grid.Row="3" Margin="155,0" />
<Label Text="Dessert" Grid.Row="3" Margin="185,20"/>
<Label Text="Type de priorité" Grid.Row="4"/>
<Grid BackgroundColor="#D1E8E2"
MinimumHeightRequest="100"
MaximumWidthRequest="300"
Padding="20"
Grid.Row="5">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Text="Recettes économiques" Grid.Row="0" Padding="5,0,0,0"/>
<BoxView Color="Black" HeightRequest="1" Margin="10,10,10,10" Grid.Row="1" />
<Label Text="Recettes rapides" Grid.Row="2"/>
<BoxView Color="Black" HeightRequest="1" Margin="10,10,10,10" Grid.Row="3" />
<Label Text="Recettes simples" Grid.Row="4"/>
<BoxView Color="Black" HeightRequest="1" Margin="10,10,10,10" Grid.Row="5" />
<Label Text="Recettes légères" Grid.Row="6"/>
<BoxView Color="Black" HeightRequest="1" Margin="10,10,10,10" Grid.Row="7" />
<Label Text="Recettes gourmandes" Grid.Row="8"/>
</Grid>
<Label Text="Saisir les étapes de la recette " Grid.Row="6" Margin="0,15"/>
<Entry Placeholder="Etape de la recette" Grid.Row="7" Margin="12,0"/>
<HorizontalStackLayout Grid.Row="8" Margin="20">
<Button WidthRequest="100" Text="Précédent" TextColor="Black" Margin="20,0,20,0"/>
<Button WidthRequest="100" Text="Ajouter" TextColor="Black" Margin="20,0"/>
</HorizontalStackLayout>
<Label Text="Saisir les ingrédients de la recette" Grid.Row="6" Grid.Column="1" Margin="50,15"/>
<HorizontalStackLayout Grid.Row="7" Grid.Column="1">
<Entry Placeholder="Nom de l'ingrédient" Margin="12,0,50,0" WidthRequest="500"/>
<Picker Title="Unité">
</Picker>
</HorizontalStackLayout>
<HorizontalStackLayout Grid.Row="8" Grid.Column="1" Margin="20">
<Button WidthRequest="100" Text="Précédent" TextColor="Black" Margin="20,0,20,0"/>
<Button WidthRequest="100" Text="Ajouter" TextColor="Black" Margin="20,0"/>
</HorizontalStackLayout>
</Grid>
</VerticalStackLayout>
</ContentPage>

@ -0,0 +1,18 @@
using CommunityToolkit.Maui.Behaviors;
using DataPersistence;
using Model;
using Model.Managers;
using System.Diagnostics;
namespace Views
{
public partial class AddRecipe : ContentPage
{
public MasterManager MasterMgr => (App.Current as App).MasterMgr;
public AddRecipe()
{
InitializeComponent();
}
}
}

@ -1,7 +1,7 @@
<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Vue"
xmlns:local="clr-namespace:Views"
x:Class="Views.App">
<Application.Resources>
<ResourceDictionary>

@ -4,34 +4,51 @@ using Microsoft.UI.Windowing;
using Windows.Graphics;
#endif
using DataPersistence;
using Model;
using System.Collections.ObjectModel;
using Model.Managers;
namespace Views
{
public partial class App : Application
{
const int WindowWidth = 1200;
const int WindowHeight = 800;
//Point d'entrée de l'application
public MasterManager MasterMgr { get; private set; } = new MasterManager(new Stubs());
//L'utilisateur courant de l'application
public User CurrentUser { get; set; }
//collection de recette de l'application
public RecipeCollection AllRecipes { get; set; }
//const int WindowWidth = 1200;
//const int WindowHeight = 800;
public App()
{
CurrentUser = MasterMgr.DataMgr.GetUsers().Last();
AllRecipes = MasterMgr.DataMgr.GetRecipes("All recipes");
InitializeComponent();
Microsoft.Maui.Handlers.WindowHandler.Mapper.AppendToMapping(nameof(IWindow), (handler, view) =>
{
#if WINDOWS
var mauiWindow = handler.VirtualView;
var nativeWindow = handler.PlatformView;
nativeWindow.Activate();
IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(nativeWindow);
WindowId windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(windowHandle);
AppWindow appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);
appWindow.Resize(new SizeInt32(WindowWidth, WindowHeight));
#endif
});
// Microsoft.Maui.Handlers.WindowHandler.Mapper.AppendToMapping(nameof(IWindow), (handler, view) =>
// {
//#if WINDOWS
// var mauiWindow = handler.VirtualView;
// var nativeWindow = handler.PlatformView;
// nativeWindow.Activate();
// IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(nativeWindow);
// WindowId windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(windowHandle);
// AppWindow appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);
// appWindow.Resize(new SizeInt32(WindowWidth, WindowHeight));
//#endif
// });
/* - Comment(ctrl-k + ctrl-c)/Uncomment(ctrl-k + ctrl-u) to change page - */
UserAppTheme = AppTheme.Light;
MainPage = new MyProfil();
MainPage = new Home();
//MainPage = new MyPosts();
}
}

@ -28,11 +28,19 @@
Style="{StaticResource button2}"
IsVisible="{Binding IsNotConnected, Source={x:Reference fl}}"
IsEnabled="{Binding IsNotConnected, Source={x:Reference fl}}"/>
<Label Text="Jean-Baptiste De La Fontaine"
HorizontalOptions="Center" Margin="15"
<StackLayout BindingContext="{Binding user}">
<Label Text="{Binding Name}"
HorizontalOptions="Center" Margin="0,15"
FontSize="20" FontAttributes="Bold" HorizontalTextAlignment="Center"
TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}"
IsVisible="{Binding IsNotConnected, Converter={toolkit:InvertedBoolConverter} ,Source={x:Reference fl}}"/>
<Label Text="{Binding Surname}"
HorizontalOptions="Center"
FontSize="20" FontAttributes="Bold" HorizontalTextAlignment="Center"
TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}"
IsVisible="{Binding IsNotConnected, Converter={toolkit:InvertedBoolConverter} ,Source={x:Reference fl}}"/>
</StackLayout>
</VerticalStackLayout>

@ -1,10 +1,20 @@
using DataPersistence;
using Model;
using Model.Managers;
namespace Views;
public partial class ContainerFlyout : ContentView
{
public MasterManager MasterMgr => (App.Current as App).MasterMgr;
public User user => (App.Current as App).CurrentUser;
public ContainerFlyout()
{
InitializeComponent();
BindingContext = this;
}
// Bind MyFlyoutContent

@ -3,6 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:local="clr-namespace:Views"
xmlns:model="clr-namespace:Model;assembly=Model"
x:Class="Views.Home">
<local:ContainerBase
@ -12,19 +13,29 @@
<local:ContainerBase.MyFlyoutContent>
<VerticalStackLayout Grid.Row="1">
<!-- Research -->
<Button Text="Recherche" ImageSource="search_icon.png"
<Button
Text="Recherche"
ImageSource="search_icon.png"
MaximumHeightRequest="20"
Style="{StaticResource button1}"/>
<SearchBar Placeholder="Mots-clés (ex.: rapide, fromage)" FontAttributes="Italic" TextColor="Black"
<SearchBar
Placeholder="Mots-clés (ex.: rapide, fromage)"
FontAttributes="Italic" TextColor="Black"
BackgroundColor="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Gray300}}"
Margin="15, 10, 15, 40"/>
<!-- Direct research -->
<Button Text="Entrées" ImageSource="flatware_icon.png"
<Button
Text="Entrées"
ImageSource="flatware_icon.png"
Style="{StaticResource button1}"/>
<Button Text="Plats" ImageSource="room_service_icon.png"
<Button
Text="Plats"
ImageSource="room_service_icon.png"
Style="{StaticResource button1}"/>
<Button Text="Desserts" ImageSource="coffee_icon.png"
<Button
Text="Desserts"
ImageSource="coffee_icon.png"
Style="{StaticResource button1}"/>
</VerticalStackLayout>
</local:ContainerBase.MyFlyoutContent>
@ -32,9 +43,13 @@
<!-- Master -->
<local:ContainerBase.MyContent>
<ScrollView>
<StackLayout>
<Label Text="Suggestions" TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource Gray100}}"
FontSize="24" Padding="15"/>
<StackLayout BindingContext="{Binding AllRecipes}" MinimumWidthRequest="400">
<!--Modification du prof apportée sur le stacklayout pour empecher l'affichage d'une seule case recipe-->
<Label
Text="{Binding Description}"
TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource Gray100}}"
FontSize="24"
Padding="15"/>
<FlexLayout
Margin="0, 15"
@ -42,8 +57,35 @@
JustifyContent="Start"
AlignItems="Center"
AlignContent="SpaceEvenly"
HorizontalOptions="Center">
HorizontalOptions="Center"
BindableLayout.ItemsSource="{Binding}">
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="model:Recipe">
<Border Style="{StaticResource recipeCase}">
<Grid RowDefinitions="*, 40">
<!--<local:RecipeCase
CaseImageSource="room_service_icon.png"
Title="{Binding Title}"/>-->
<Image
Grid.Row="0" VerticalOptions="Fill"
Source="{Binding Image}"/>
<Label
Text="{Binding Title}" FontSize="18"
Grid.Row="1" HorizontalOptions="Center"/>
</Grid>
</Border>
</DataTemplate>
</BindableLayout.ItemTemplate>
<!--<local:RecipeCase CaseImageSource="room_service_icon.png"/>
<local:RecipeCase CaseImageSource="room_service_icon.png"/>
<local:RecipeCase CaseImageSource="room_service_icon.png"/>
<local:RecipeCase CaseImageSource="room_service_icon.png"/>
@ -53,8 +95,7 @@
<local:RecipeCase CaseImageSource="room_service_icon.png"/>
<local:RecipeCase CaseImageSource="room_service_icon.png"/>
<local:RecipeCase CaseImageSource="room_service_icon.png"/>
<local:RecipeCase CaseImageSource="room_service_icon.png"/>
<local:RecipeCase CaseImageSource="room_service_icon.png"/>
<local:RecipeCase CaseImageSource="room_service_icon.png"/>-->
</FlexLayout>
</StackLayout>

@ -1,10 +1,22 @@
namespace Views
using DataPersistence;
using Model;
using Model.Managers;
namespace Views
{
public partial class Home : ContentPage
{
public MasterManager MasterMgr => (App.Current as App).MasterMgr;
public User user => (App.Current as App).CurrentUser;
public RecipeCollection AllRecipes => (App.Current as App).AllRecipes;
public Home()
{
//DataMgr = new DataManager(new Stubs());
//AllRecipes = new RecipeCollection("Toutes les recettes", DataMgr.Data[nameof(Recipe)].Cast<Recipe>().ToArray());
InitializeComponent();
BindingContext = this;
}
}
}

@ -14,8 +14,8 @@
<Grid RowDefinitions="250, *, *" VerticalOptions="Fill">
<VerticalStackLayout Grid.Row="1">
<Button Text="Mes informations" ImageSource="person_default.png" Style="{StaticResource button1}" Grid.Row="1"/>
<Button Text="Modifier" ImageSource="settings_icon.png" Style="{StaticResource button1}" Grid.Row="2"/>
<Button Text="Mes Recettes" ImageSource="person_default.png" Style="{StaticResource button1}" Grid.Row="1"/>
<Button Text="Ajouter Recette" ImageSource="settings_icon.png" Style="{StaticResource button1}" Grid.Row="2"/>
</VerticalStackLayout>
</Grid>
@ -24,7 +24,8 @@
<local:ContainerBase.MyContent>
<ScrollView>
<StackLayout BindingContext="User"><!--Attention debut de binding-->
<StackLayout >
<!--user's informations-->
<Label Text="Mon profil" TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource Gray100}}"
FontAttributes="Bold"
FontSize="24" Padding="15, 15, 20, 5"/>
@ -36,30 +37,22 @@
Padding="50,0,0,0"
FontSize="18"/>
<Entry BackgroundColor="#D1E8E2"
Margin="50,10,0,20"/>
Margin="50,10,0,20"
Text="{Binding CurrentUser.Name}"/>
<Label Text="Prénom :"
Padding="50,0,0,0"
FontSize="18"/>
<Entry BackgroundColor="#D1E8E2"
Margin="50,10,0,20"/>
Margin="50,10,0,20"
Text="{Binding CurrentUser.Surname} "/>
<Label Text="Mail :"
Padding="50,0,0,0"
FontSize="18"/>
<Entry BackgroundColor="#D1E8E2"
Margin="50,10,0,20"/>
<Label Text="Pseudo :"
Padding="50,0,0,0"
FontSize="18"/>
<Entry BackgroundColor="#D1E8E2"
Margin="50,10,0,50"/>
<Button BackgroundColor="#bdf5bd"
Text="Valider"
Margin="40,0,0,0"
TextColor="Black"
MaximumWidthRequest="100"/>
Margin="50,10,0,20"
IsEnabled="False"
Text="{Binding CurrentUser.Mail}"/>
<!--liste drag and drop-->
</VerticalStackLayout>
<VerticalStackLayout Padding="100,0,0,0">
<Label Text="Priorités du compte : " TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource Gray100}}"
@ -91,20 +84,10 @@
<BoxView Color="Black" HeightRequest="1" Margin="10,10,10,10" Grid.Row="7" />
<Label Text="Recettes gourmandes" Grid.Row="8"/>
</Grid>
</VerticalStackLayout>
</HorizontalStackLayout>
</StackLayout>
</ScrollView>
</local:ContainerBase.MyContent>
</local:ContainerBase>
</ContentPage>

@ -1,9 +1,20 @@
using CommunityToolkit.Maui.Behaviors;
using DataPersistence;
using Model;
using Model.Managers;
using System.Diagnostics;
namespace Views;
public partial class MyProfil : ContentPage
{
public MasterManager MasterMgr => (App.Current as App).MasterMgr;
public User user => (App.Current as App).CurrentUser;
public MyProfil()
{
InitializeComponent();
BindingContext = this;
}
}

@ -57,10 +57,14 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DataPersistence\DataPersistence.csproj" />
<ProjectReference Include="..\Model\Model.csproj" />
</ItemGroup>
<ItemGroup>
<MauiXaml Update="AddRecipe.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="ContainerBase.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>

@ -9,4 +9,23 @@
# SAE 2.01 - Développement d'une application
## Ma Cuisine Trop Géniale ! - Application MAUI
### Ma Cuisine Trop Géniale ! - Application MAUI
<br>
---
## Documentation
La conception et les diagrammes concernant l'architecture du projet sont disponible dans [le wiki](https://codefirst.iut.uca.fr/git/alexandre.agostinho/SAE-2.01/wiki/).
---
## Tests fonctionnels
Navigation dans les menus de l'application console :
- ` ^ ` *(fleche haute)* : selection précédante,
- ` v ` *(fleche basse)* : selection suivente,
- ` < ` *(fleche gauche)* : retour arrière,
- ` <enter> ` *(touche entrée)* : entrer dans la séléction / valider,
- ` r ` *(touche 'r')* : **activer le mode écriture**: rechercher dans les sélections / mettre du texte dans les zones de textes,
- ` <echap> ` *(touche echap)* : **désactiver le mode écriture** / **enregistrer le texte dans la zone de texte**.
⚠️ La navigation dans les menus de l'application console est assez spéciale. Elle est plutot complexe et surtout très mal optimisée (comme ce n'est que pour tester). Par exemple, pour entrer du texte dans un champ de texte : il faut activer le **mode écriture** avec la touche `r`, entrer son texte, **PUIS désactiver le mode écriture** avec la touche `<echap>`. Cette dernière étape permet d'enregistrer le texte dans le champ.
Loading…
Cancel
Save