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,12 +5,11 @@
<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>
<ProjectReference Include="..\DataPersistence\DataPersistence.csproj" />
<ProjectReference Include="..\Model\Model.csproj" />
</ItemGroup>
</Project>

@ -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;
}
}
}

@ -1,24 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp.Menu
{
internal class ConnectionMenu : Entry
{
public ConnectionMenu()
: base("Connection",
new Entry.EntryStep("Username: ", typeof(string)),
new Entry.EntryStep("Password: ", typeof(string)))
{ }
public override IMenu? Return()
{
string username = _selectList[0].Item.Input;
string password = _selectList[1].Item.Input;
return new PlainText($"User: {username} connected with password: {password}");
}
}
}
using System;
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
{
private MasterManager _masterMgr;
private bool _wrongInput = false;
public ConnectionMenu(MasterManager masterManager)
: base("Connection",
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 mail = _selectList[0].Item.Input;
string password = _selectList[1].Item.Input;
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,119 +1,151 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp.Menu
{
/// <summary>
/// Define an Entry menu.
/// <br/>It allows you to navigate through the entries and completes them with a console input.
/// </summary>
internal abstract partial class Entry : Menu<Entry.EntryStep>
{
#region Attributes & Properties
private List<EntryStep> _steps;
#endregion
#region Constructors
/// <summary>
/// Constructor of the entry menu, based on the Menu constructor.
/// </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)
{
_steps = entrySteps.ToList();
_allSelectors = ConvertEntryStepsInSelector();
_selectList = _allSelectors;
}
#endregion
#region Methods
private List<Selector<EntryStep>> ConvertEntryStepsInSelector()
{
List<Selector<EntryStep>> newSelectors = new List<Selector<EntryStep>>();
foreach (EntryStep step in _steps)
{
newSelectors.Add(new Selector<EntryStep>(step, step.Description));
}
return newSelectors;
}
#endregion
#region IMenu implementation
public override void WriteMenuMode(ConsoleKeyInfo cki)
{
if (!WriteMode && cki.Key == ConsoleKey.R)
{
EnableWriteMode();
return;
}
if (WriteMode)
{
if (cki.Key == ConsoleKey.Escape)
{
if (CurrentSelected is null)
throw new ArgumentNullException("CurrentSelected");
CurrentSelected.Input = InputStr.ToString();
DisableWriteMode();
InputStr.Clear();
return;
}
if (cki.Key == ConsoleKey.Backspace && InputStr.Length > 0)
{
InputStr.Remove(InputStr.Length - 1, 1);
return;
}
InputStr.Append(cki.KeyChar);
}
}
public override void Update()
{
if (_selectList.Count == 0)
{
CurrentSelected = default;
return;
}
CurrentSelected = _selectList[CurrentLine].Item;
}
public override void Display()
{
_screenDisplay.Clear();
Console.Clear();
_screenDisplay.AppendLine($"[ {Title} ]");
_screenDisplay.AppendLine("-------------------------------------------");
for (int i = 0; i < _selectList.Count; i++)
{
if (CurrentLine == i)
_screenDisplay.Append($"> ");
else
_screenDisplay.Append($" ");
_screenDisplay.Append($"{_selectList[i].Line} {_selectList[i].Item.Input}");
if (CurrentLine == i && WriteMode)
_screenDisplay.Append(InputStr);
_screenDisplay.AppendLine();
}
if (_selectList.Count == 0)
_screenDisplay.AppendLine("Empty...");
_screenDisplay.AppendLine(
"\n\nHint:\n^:previous, v:next, <:ret, -enter-:return, r:write, -escape-:exit search mode");
Console.WriteLine(_screenDisplay);
}
#endregion
}
}
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.Core
{
/// <summary>
/// Define an Entry menu.
/// <br/>It allows you to navigate through the entries and completes them with a console input.
/// </summary>
internal abstract partial class Entry : Menu<Entry.EntryStep>
{
#region Attributes & Properties
private List<EntryStep> _steps;
#endregion
#region Constructors
/// <summary>
/// Constructor of the entry menu, based on the Menu constructor.
/// </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)
{
_steps = entrySteps.ToList();
_allSelectors = ConvertEntryStepsInSelector();
_selectList = _allSelectors;
}
#endregion
#region Methods
private List<Selector<EntryStep>> ConvertEntryStepsInSelector()
{
List<Selector<EntryStep>> newSelectors = new List<Selector<EntryStep>>();
foreach (EntryStep step in _steps)
{
newSelectors.Add(new Selector<EntryStep>(step, step.Description));
}
return newSelectors;
}
#endregion
#region IMenu implementation
public override void WriteMenuMode(ConsoleKeyInfo cki)
{
if (!WriteMode && cki.Key == ConsoleKey.R)
{
EnableWriteMode();
if (CurrentSelected is null)
return;
InputStr.Append(CurrentSelected.Input);
CurrentSelected.Input = "";
return;
}
if (WriteMode)
{
if (cki.Key == ConsoleKey.Escape)
{
if (CurrentSelected is null)
throw new ArgumentNullException("CurrentSelected");
CurrentSelected.Input = InputStr.ToString();
DisableWriteMode();
InputStr.Clear();
return;
}
if (cki.Key == ConsoleKey.Backspace)
{
if (InputStr.Length > 0) InputStr.Remove(InputStr.Length - 1, 1);
return;
}
InputStr.Append(cki.KeyChar);
}
}
public override void Update()
{
if (_selectList.Count == 0)
{
CurrentSelected = default;
return;
}
CurrentSelected = _selectList[CurrentLine].Item;
}
public override void Display()
{
StringBuilder displayItem = new StringBuilder();
_screenDisplay.Clear();
Console.Clear();
_screenDisplay.AppendLine($"[ {Title} ]");
_screenDisplay.AppendLine("-------------------------------------------");
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} {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, <: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);
}

@ -3,8 +3,8 @@ using System.Collections.Generic;
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);
@ -36,16 +36,16 @@ namespace ConsoleApp.Menu
public StringBuilder InputStr { get; set; }
public void DisableWriteMode()
{
// Plain text does not need to do anything for this.
{
// Plain text does not need to do anything for this.
}
public void EnableWriteMode()
{
// Plain text does not need to do anything for this.
{
// Plain text does not need to do anything for this.
}
public void SelectNext()
{
// Plain text does not need to do anything for this.
{
// Plain text does not need to do anything for this.
}
public void SelectPrevious()
{

@ -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();
}
}
}

@ -1,24 +1,58 @@
using Model;
using DataPersistence;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp.Menu
{
/// <summary>
/// Main menu of the console. Contain the first interaction menus.
/// </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"))
{ }
}
}
using Model;
using DataPersistence;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConsoleApp.Menu.Core;
using Model.Managers;
namespace ConsoleApp.Menu
{
/// <summary>
/// Main menu of the console. Contain the first interaction menus.
/// </summary>
internal class MainMenu : Menu<IMenu>
{
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 ConsoleApp;
using Model;
using DataPersistence;
using Model;
using System.Linq;
using System.Text;
Console.WriteLine("Hello, World!\n\n");
// TESTS:
DataManager dataMgr = new DataManager(new Stubs());
//DataManager dataMgr = new DataManager(new DataContractXML());
//DataManager dataMgr = new DataManager(new DataContractJSON());
using Model.Managers;
dataMgr.Serializer = new DataContractXML();
//dataMgr.Serializer = new DataContractJSON();
Console.WriteLine("Hello, World!\n\n");
// /!\ 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");
string path = ""; // - path to the save file
string strategy = "xml"; // - strategy is 'xml' or 'json' (/!\ this is case sensitive)
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());
MasterManager masterMgr;
IDataManager dataManager = (strategy == "xml") ?
new DataContractXML(path)
: new DataContractJSON(path);
User user = dataMgr.GetUsers().Last();
if (!File.Exists(Path.Combine(path, $"data.{strategy}")))
{
masterMgr = new MasterManager(new Stubs());
masterMgr.DataMgr.Serializer = dataManager;
}
else
{
masterMgr = new MasterManager(dataManager);
}
//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 !"));
dataMgr.Save();
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
};

@ -1,137 +1,147 @@
using Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DataPersistence
{
/// <summary>
/// The subs class is a group of prefabricated object that can only be loaded. It only use is for testing.
/// </summary>
public class Stubs : IDataManager
{
public Dictionary<string, List<object>> Load()
{
PasswordManager passwordManager = new PasswordManager();
Dictionary<string, List<object>> data = new Dictionary<string, List<object>>
{
{
#region Data: Recipes
nameof(Recipe),
new List<object>(new[]
{
new Recipe(
title: "Cookies classiques", id: null,
ingredients: new List<Ingredient>(new[]
using Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DataPersistence
{
/// <summary>
/// The subs class is a group of prefabricated object that can only be loaded. It only use is for testing.
/// </summary>
public class Stubs : IDataManager
{
public Dictionary<string, List<object>> Load()
{
Dictionary<string, List<object>> data = new Dictionary<string, List<object>>
{
{
#region Data: Recipes
nameof(Recipe),
new List<object>(new[]
{
new Recipe(
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)),
new Ingredient("Farine", new Quantity(23, Unit.G))
}),
preparationSteps: new[]
{
new PreparationStep(1, "Faire cuire."),
new PreparationStep(2, "Manger.")
}),
new Recipe(
title: "Cookies au chocolat", id: null,
preparationSteps: new[]
{
new PreparationStep(1, "Moulinez la pâte."),
new PreparationStep(2, "Faire cuire pendant une bonne heure."),
new PreparationStep(3, "Sortir du four et mettre dans un plat.")
}),
new Recipe(
title: "Gateau nature", id: null,
preparationSteps: new[]
{
new PreparationStep(1, "Achetez les ingrédients."),
new PreparationStep(2, "Préparez le matériel. Ustensiles et tout."),
new PreparationStep(3, "Pleurez.")
}),
new Recipe(
title: "Gateau au pommes", id: null,
preparationSteps: new[]
{
new PreparationStep(1, "Achetez les légumes."),
new PreparationStep(2, "Préparez le plat. Ustensiles et préchauffez le four."),
new PreparationStep(3, "Coupez les pommes en morceaux et disposez-les sur le plat."),
new PreparationStep(4, "Mettez enfin le plat au four, puis une fois cuit, dégustez !")
}),
new Recipe(
title: "Gateau au chocolat", id: null,
preparationSteps: new[]
{
new PreparationStep(1, "Ajouter les oeufs."),
new PreparationStep(2, "Ajouter la farine."),
new PreparationStep(3, "Ajouter 100g de chocolat fondu."),
new PreparationStep(4, "Mélanger le tout."),
new PreparationStep(5, "Faire cuire 45h au four traditionnel.")
}),
new Recipe(
title: "Dinde au jambon", id: null,
preparationSteps: new[]
{
new PreparationStep(1, "Faire une cuisson bien sec de la dinde à la poêle"),
new PreparationStep(2, "Mettre la dinde au frigo."),
new PreparationStep(3, "Mettre le jambon dans le micro-onde."),
new PreparationStep(4, "Faire chauffer 3min."),
new PreparationStep(5, "Présentez sur un plat la dinde et le jambon : Miam !")
}),
new Recipe(
title: "Poulet au curry", id: null,
preparationSteps: new[]
{
new PreparationStep(1, "Trouvez des épices de curry."),
new PreparationStep(2, "Trouvez maintenant du poulet."),
new PreparationStep(3, "Coupez la tête du poulet et posez-la dans un plat."),
new PreparationStep(4, "Parsemez d'épices curry la tête de la poule."),
new PreparationStep(5, "Mettre le tout au four traditionnel 30min."),
new PreparationStep(6, "Dégustez en famille !")
})
})
#endregion
},
}),
preparationSteps: new[]
{
new PreparationStep(1, "Faire cuire."),
new PreparationStep(2, "Manger.")
}),
new Recipe(
authorMail: "admin@mctg.fr",
title: "Cookies au chocolat", id: null,
preparationSteps: new[]
{
new PreparationStep(1, "Moulinez la pâte."),
new PreparationStep(2, "Faire cuire pendant une bonne heure."),
new PreparationStep(3, "Sortir du four et mettre dans un plat.")
}),
new Recipe(
title: "Gateau nature", id: null,
authorMail: "admin@mctg.fr",
preparationSteps: new[]
{
new PreparationStep(1, "Achetez les ingrédients."),
new PreparationStep(2, "Préparez le matériel. Ustensiles et tout."),
new PreparationStep(3, "Pleurez.")
}),
new Recipe(
title: "Gateau au pommes", id: null,
authorMail: "admin@mctg.fr",
preparationSteps: new[]
{
new PreparationStep(1, "Achetez les légumes."),
new PreparationStep(2, "Préparez le plat. Ustensiles et préchauffez le four."),
new PreparationStep(3, "Coupez les pommes en morceaux et disposez-les sur le plat."),
new PreparationStep(4, "Mettez enfin le plat au four, puis une fois cuit, dégustez !")
}),
new Recipe(
title: "Gateau au chocolat", id: null,
authorMail: "pedrosamigos@hotmail.com",
preparationSteps: new[]
{
new PreparationStep(1, "Ajouter les oeufs."),
new PreparationStep(2, "Ajouter la farine."),
new PreparationStep(3, "Ajouter 100g de chocolat fondu."),
new PreparationStep(4, "Mélanger le tout."),
new PreparationStep(5, "Faire cuire 45h au four traditionnel.")
}),
new Recipe(
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"),
new PreparationStep(2, "Mettre la dinde au frigo."),
new PreparationStep(3, "Mettre le jambon dans le micro-onde."),
new PreparationStep(4, "Faire chauffer 3min."),
new PreparationStep(5, "Présentez sur un plat la dinde et le jambon : Miam !")
}),
new Recipe(
title: "Poulet au curry", id: null,
authorMail: "pedrosamigos@hotmail.com",
preparationSteps: new[]
{
new PreparationStep(1, "Trouvez des épices de curry."),
new PreparationStep(2, "Trouvez maintenant du poulet."),
new PreparationStep(3, "Coupez la tête du poulet et posez-la dans un plat."),
new PreparationStep(4, "Parsemez d'épices curry la tête de la poule."),
new PreparationStep(5, "Mettre le tout au four traditionnel 30min."),
new PreparationStep(6, "Dégustez en famille !")
})
})
#endregion
},
{
#region Data: User
nameof(User),
#region Data: User
nameof(User),
new List<object>(new[]
{
new User(
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
}
};
return data;
}
#region Not supported methods
public void Save(Dictionary<string, List<object>> elements)
{
throw new NotSupportedException();
}
public void Export<T>(T obj, string pathToExport) where T : class
{
throw new NotSupportedException();
}
public KeyValuePair<string, T> Import<T>(string pathToImport) where T : class
{
throw new NotSupportedException();
}
#endregion
}
}
}
};
return data;
}
#region Not supported methods
public void Save(Dictionary<string, List<object>> elements)
{
throw new NotSupportedException();
}
public void Export<T>(T obj, string pathToExport) where T : class
{
throw new NotSupportedException();
}
public KeyValuePair<string, T> Import<T>(string pathToImport) where T : class
{
throw new NotSupportedException();
}
#endregion
}
}

@ -1,105 +1,105 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model
{
/// <summary>
/// Define the manager of the data. This is where all the data are put, and where we call the loading and the saving of them.
/// </summary>
public class DataManager
{
#region Attributes & Properties
/// <summary>
/// The data manager injected that know how to serialize the data.
/// <br/><remarks><i>The setter is actually public for testing purpose. It will be private after.</i></remarks>
/// <br/>See: <see cref="IDataManager"/>
/// </summary>
public IDataManager Serializer { get; set; }
/// <summary>
/// The collection of all data. Each line of this dictionary has the type of the data as it key and the data for values.
/// </summary>
public Dictionary<string, List<object>> Data { get; private set; }
#endregion
#region Constructors
/// <summary>
/// Constructor of the DataManager class. Take a IDataManager that will provide methods for the serialisation of the data.
/// </summary>
/// <param name="dataMgr">The data manager that know how to serialize a file.</param>
public DataManager(IDataManager dataMgr)
{
Serializer = dataMgr;
Data = Serializer.Load();
}
#endregion
#region Methods
/// <summary>
/// Reload the data. Useful to update new data written in the save file.
/// <br/>See: <see cref="IDataManager.Load"/>
/// </summary>
public void Reload()
=> Data = Serializer.Load();
/// <summary>
/// Save the data. Call the Save method of the serializer.
/// <br/>See: <see cref="IDataManager.Save(Dictionary{string, List{object}})"/>
/// </summary>
public void Save()
=> Serializer.Save(Data);
/// <summary>
/// Import data from a file.
/// <br/>See: <see cref="IDataManager.Import{T}(string)"/>
/// </summary>
/// <typeparam name="T">The type of data to import.</typeparam>
/// <param name="pathOfTheFile">The path containing the name of the file created.</param>
public void Import<T>(string pathOfTheFile)
where T : class
{
KeyValuePair<string, T> import = Serializer.Import<T>(pathOfTheFile);
Data[import.Key].Add(import.Value);
}
/// <summary>
/// Export the data from the collection of data.
/// <br/>See: <see cref="IDataManager.Export{T}(T, string)"/>
/// </summary>
/// <typeparam name="T">The type of data to export</typeparam>
/// <param name="obj">The object to export</param>
/// <param name="pathToExport">The path containing the name of the file created.</param>
public void Export<T>(T obj, string pathToExport)
where T : class
=> Serializer.Export<T>(obj, pathToExport);
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model
{
/// <summary>
/// Define the manager of the data. This is where all the data are put, and where we call the loading and the saving of them.
/// </summary>
public class DataManager
{
#region Attributes & Properties
/// <summary>
/// The data manager injected that know how to serialize the data.
/// <br/><remarks><i>The setter is actually public for testing purpose. It will be private after.</i></remarks>
/// <br/>See: <see cref="IDataManager"/>
/// </summary>
public IDataManager Serializer { get; set; }
/// <summary>
/// The collection of all data. Each line of this dictionary has the type of the data as it key and the data for values.
/// </summary>
public Dictionary<string, List<object>> Data { get; private set; }
#endregion
#region Constructors
/// <summary>
/// Constructor of the DataManager class. Take a IDataManager that will provide methods for the serialisation of the data.
/// </summary>
/// <param name="dataMgr">The data manager that know how to serialize a file.</param>
public DataManager(IDataManager dataMgr)
{
Serializer = dataMgr;
Data = Serializer.Load();
}
#endregion
#region Methods
/// <summary>
/// Reload the data. Useful to update new data written in the save file.
/// <br/>See: <see cref="IDataManager.Load"/>
/// </summary>
public void Reload()
=> Data = Serializer.Load();
/// <summary>
/// Save the data. Call the Save method of the serializer.
/// <br/>See: <see cref="IDataManager.Save(Dictionary{string, List{object}})"/>
/// </summary>
public void Save()
=> Serializer.Save(Data);
/// <summary>
/// Import data from a file.
/// <br/>See: <see cref="IDataManager.Import{T}(string)"/>
/// </summary>
/// <typeparam name="T">The type of data to import.</typeparam>
/// <param name="pathOfTheFile">The path containing the name of the file created.</param>
public void Import<T>(string pathOfTheFile)
where T : class
{
KeyValuePair<string, T> import = Serializer.Import<T>(pathOfTheFile);
Data[import.Key].Add(import.Value);
}
/// <summary>
/// Export the data from the collection of data.
/// <br/>See: <see cref="IDataManager.Export{T}(T, string)"/>
/// </summary>
/// <typeparam name="T">The type of data to export</typeparam>
/// <param name="obj">The object to export</param>
/// <param name="pathToExport">The path containing the name of the file created.</param>
public void Export<T>(T obj, string pathToExport)
where T : class
=> Serializer.Export<T>(obj, pathToExport);
/// <summary>
/// Get all the recipe from the data.
/// </summary>
/// <param name="rcTitle">The title to give for the Recipe Collection</param>
/// <returns>A RecipeCollection that contain all the recipe in the data.</returns>
/// <returns>A RecipeCollection that contain all the recipe in the data.</returns>
public RecipeCollection GetRecipes(string rcTitle = "default")
=> new RecipeCollection(rcTitle, Data[nameof(Recipe)].Cast<Recipe>().ToArray());
=> new RecipeCollection(rcTitle, Data[nameof(Recipe)].Cast<Recipe>().ToArray());
/// <summary>
/// Get all the Users from the data.
/// </summary>
/// <returns>A list of all Users.</returns>
public List<User> GetUsers()
=> new List<User>(Data[nameof(User)].Cast<User>());
/// <returns>A list of all Users.</returns>
public List<User> GetUsers()
=> new List<User>(Data[nameof(User)].Cast<User>());
/// <summary>
/// Get a list of an item in the data.
/// </summary>
/// <typeparam name="T">The type of the item</typeparam>
/// <returns>The list of all the item found in the data.</returns>
public ICollection<T> GetFromData<T>() where T : class
=> new List<T>(Data[typeof(T).Name].Cast<T>());
#endregion
}
}
/// <returns>The list of all the item found in the data.</returns>
public ICollection<T> GetFromData<T>() where T : class
=> new List<T>(Data[typeof(T).Name].Cast<T>());
#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
set
{
if (string.IsNullOrWhiteSpace(value))
{
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>

@ -1,38 +1,55 @@
#if WINDOWS
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Windows.Graphics;
#endif
namespace Views
{
public partial class App : Application
{
const int WindowWidth = 1200;
const int WindowHeight = 800;
public App()
{
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
});
/* - Comment(ctrl-k + ctrl-c)/Uncomment(ctrl-k + ctrl-u) to change page - */
UserAppTheme = AppTheme.Light;
MainPage = new MyProfil();
//MainPage = new MyPosts();
}
}
}
#if WINDOWS
using Microsoft.UI;
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
{
//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
// });
/* - Comment(ctrl-k + ctrl-c)/Uncomment(ctrl-k + ctrl-u) to change page - */
UserAppTheme = AppTheme.Light;
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,39 +1,49 @@
namespace Views;
public partial class ContainerFlyout : ContentView
{
public ContainerFlyout()
{
InitializeComponent();
}
// Bind MyFlyoutContent
public static readonly BindableProperty MyFlyoutContentProperty =
BindableProperty.Create("MyFlyoutContent", typeof(View), typeof(ContainerFlyout), new Grid());
public View MyFlyoutContent
{
get => (View)GetValue(MyFlyoutContentProperty);
set => SetValue(MyFlyoutContentProperty, value);
}
// Bind IsNotConnected
public static readonly BindableProperty IsNotConnectedProperty =
BindableProperty.Create("IsNotConnected", typeof(bool), typeof(Button), true);
public bool IsNotConnected
{
get => (bool)GetValue(IsNotConnectedProperty);
set => SetValue(IsNotConnectedProperty, value);
}
// bind NeedReturn
public static readonly BindableProperty NeedReturnProperty =
BindableProperty.Create("NeedReturn", typeof(bool), typeof(Border), false);
public bool NeedReturn
{
get => (bool)GetValue(NeedReturnProperty);
set => SetValue(NeedReturnProperty, value);
}
}
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
public static readonly BindableProperty MyFlyoutContentProperty =
BindableProperty.Create("MyFlyoutContent", typeof(View), typeof(ContainerFlyout), new Grid());
public View MyFlyoutContent
{
get => (View)GetValue(MyFlyoutContentProperty);
set => SetValue(MyFlyoutContentProperty, value);
}
// Bind IsNotConnected
public static readonly BindableProperty IsNotConnectedProperty =
BindableProperty.Create("IsNotConnected", typeof(bool), typeof(Button), true);
public bool IsNotConnected
{
get => (bool)GetValue(IsNotConnectedProperty);
set => SetValue(IsNotConnectedProperty, value);
}
// bind NeedReturn
public static readonly BindableProperty NeedReturnProperty =
BindableProperty.Create("NeedReturn", typeof(bool), typeof(Border), false);
public bool NeedReturn
{
get => (bool)GetValue(NeedReturnProperty);
set => SetValue(NeedReturnProperty, value);
}
}

@ -1,66 +1,107 @@
<?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"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:local="clr-namespace:Views"
x:Class="Views.Home">
<local:ContainerBase
IsNotConnected="True">
<!-- Flyout -->
<local:ContainerBase.MyFlyoutContent>
<VerticalStackLayout Grid.Row="1">
<!-- Research -->
<Button Text="Recherche" ImageSource="search_icon.png"
MaximumHeightRequest="20"
Style="{StaticResource button1}"/>
<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"
Style="{StaticResource button1}"/>
<Button Text="Plats" ImageSource="room_service_icon.png"
Style="{StaticResource button1}"/>
<Button Text="Desserts" ImageSource="coffee_icon.png"
Style="{StaticResource button1}"/>
</VerticalStackLayout>
</local:ContainerBase.MyFlyoutContent>
<!-- Master -->
<local:ContainerBase.MyContent>
<ScrollView>
<StackLayout>
<Label Text="Suggestions" TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource Gray100}}"
FontSize="24" Padding="15"/>
<FlexLayout
Margin="0, 15"
Wrap="Wrap"
JustifyContent="Start"
AlignItems="Center"
AlignContent="SpaceEvenly"
HorizontalOptions="Center">
<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"/>
<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>
</ScrollView>
</local:ContainerBase.MyContent>
</local:ContainerBase>
</ContentPage>
<?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"
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
IsNotConnected="True">
<!-- Flyout -->
<local:ContainerBase.MyFlyoutContent>
<VerticalStackLayout Grid.Row="1">
<!-- Research -->
<Button
Text="Recherche"
ImageSource="search_icon.png"
MaximumHeightRequest="20"
Style="{StaticResource button1}"/>
<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"
Style="{StaticResource button1}"/>
<Button
Text="Plats"
ImageSource="room_service_icon.png"
Style="{StaticResource button1}"/>
<Button
Text="Desserts"
ImageSource="coffee_icon.png"
Style="{StaticResource button1}"/>
</VerticalStackLayout>
</local:ContainerBase.MyFlyoutContent>
<!-- Master -->
<local:ContainerBase.MyContent>
<ScrollView>
<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"
Wrap="Wrap"
JustifyContent="Start"
AlignItems="Center"
AlignContent="SpaceEvenly"
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"/>
<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"/>
<local:RecipeCase CaseImageSource="room_service_icon.png"/>-->
</FlexLayout>
</StackLayout>
</ScrollView>
</local:ContainerBase.MyContent>
</local:ContainerBase>
</ContentPage>

@ -1,10 +1,22 @@
namespace Views
{
public partial class Home : ContentPage
{
public Home()
{
InitializeComponent();
}
}
}
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}}"
@ -67,44 +60,34 @@
<Grid BackgroundColor="#D1E8E2"
MinimumHeightRequest="300"
Padding="20">
<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"/>
<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"/>
<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"/>
<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"/>
<Label Text="Recettes gourmandes" Grid.Row="8"/>
</Grid>
</VerticalStackLayout>
</HorizontalStackLayout>
</StackLayout>
</ScrollView>
</local:ContainerBase.MyContent>
</local:ContainerBase>
</ContentPage>

@ -1,9 +1,20 @@
namespace Views;
public partial class MyProfil : ContentPage
{
public MyProfil()
{
InitializeComponent();
}
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