diff --git a/DrinksInfo.m1chael888/DrinksInfo.m1chael888.slnx b/DrinksInfo.m1chael888/DrinksInfo.m1chael888.slnx
new file mode 100644
index 00000000..c0efd021
--- /dev/null
+++ b/DrinksInfo.m1chael888/DrinksInfo.m1chael888.slnx
@@ -0,0 +1,3 @@
+
+
+
diff --git a/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Controllers/DrinksController.cs b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Controllers/DrinksController.cs
new file mode 100644
index 00000000..bab50d35
--- /dev/null
+++ b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Controllers/DrinksController.cs
@@ -0,0 +1,88 @@
+using DrinksInfo.m1chael888.Views;
+using DrinksInfo.m1chael888.Services;
+using DrinksInfo.m1chael888.Models;
+using static DrinksInfo.m1chael888.Enums.MainMenuEnums;
+using Spectre.Console;
+
+namespace DrinksInfo.m1chael888.Controllers;
+
+public class DrinksController
+{
+ private readonly IDrinksView _drinksView;
+ private readonly IDrinksService _drinksService;
+ public DrinksController(IDrinksView tableView, IDrinksService drinksService)
+ {
+ _drinksView = tableView;
+ _drinksService = drinksService;
+ }
+
+ public void HandleMainMenu()
+ {
+ var choice = _drinksView.ShowMainMenu();
+
+ switch (choice)
+ {
+ case MainMenuOption.ViewCategories:
+ try
+ {
+ HandleCategoryChoice();
+ }
+ catch (Exception ex)
+ {
+ _drinksView.ShowAccessError(ex.Message);
+ }
+ break;
+ case MainMenuOption.Exit:
+ Environment.Exit(0);
+ break;
+ }
+ }
+
+ private void HandleCategoryChoice()
+ {
+ var categories = _drinksService.GetCategories();
+ if (categories.Count == 0)
+ {
+ ReturnStatus("Drinks menu currently unnavailable");
+ }
+ else
+ {
+ var categoryChoice = _drinksView.ShowCategoryPrompt(categories);
+ try
+ {
+ HandleDrinkChoice(categoryChoice);
+ }
+ catch (Exception ex)
+ {
+ _drinksView.ShowAccessError(ex.Message);
+ }
+ }
+ }
+
+ private void HandleDrinkChoice(Category categoryChoice)
+ {
+ var drinks = _drinksService.GetDrinks(categoryChoice.strCategory);
+ if (drinks.Count == 0)
+ {
+ ReturnStatus("Drinks menu currently unnavailable");
+ }
+ else
+ {
+ var drinkChoice = _drinksView.ShowDrinkPrompt(drinks);
+ _drinksView.ShowDrinkInfo(drinkChoice);
+ }
+
+ }
+
+ private void ReturnStatus(string msg)
+ {
+ AnsiConsole.MarkupLine($"[cyan]{msg}[/]");
+ AnsiConsole.Status()
+ .Spinner(Spinner.Known.Point)
+ .SpinnerStyle("white")
+ .Start($"[grey74]Press any key to return[/]", x =>
+ {
+ Console.ReadKey();
+ });
+ }
+}
\ No newline at end of file
diff --git a/DrinksInfo.m1chael888/DrinksInfo.m1chael888/DrinksInfo.m1chael888.csproj b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/DrinksInfo.m1chael888.csproj
new file mode 100644
index 00000000..bc546891
--- /dev/null
+++ b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/DrinksInfo.m1chael888.csproj
@@ -0,0 +1,17 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
diff --git a/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Enums/Extensions.cs b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Enums/Extensions.cs
new file mode 100644
index 00000000..83cfdbfa
--- /dev/null
+++ b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Enums/Extensions.cs
@@ -0,0 +1,17 @@
+using System.ComponentModel;
+
+namespace DrinksInfo.m1chael888.Enums;
+
+public static class Extensions
+{
+ public static string GetDescription(Enum value)
+ {
+ var field = value.GetType().GetField(value.ToString());
+ var attributes = (DescriptionAttribute[])field.GetCustomAttributes(typeof(DescriptionAttribute), false);
+ if (attributes.Length > 0)
+ {
+ return attributes[0].Description;
+ }
+ return value.ToString();
+ }
+}
diff --git a/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Enums/MainMenuEnums.cs b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Enums/MainMenuEnums.cs
new file mode 100644
index 00000000..d3bd8ac8
--- /dev/null
+++ b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Enums/MainMenuEnums.cs
@@ -0,0 +1,14 @@
+using System.ComponentModel;
+
+namespace DrinksInfo.m1chael888.Enums;
+
+public static class MainMenuEnums
+{
+ public enum MainMenuOption
+ {
+ [Description("View Categories")]
+ ViewCategories,
+ [Description("Exit App")]
+ Exit
+ }
+}
diff --git a/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Infrastructure/Router.cs b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Infrastructure/Router.cs
new file mode 100644
index 00000000..3d3b94a8
--- /dev/null
+++ b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Infrastructure/Router.cs
@@ -0,0 +1,18 @@
+using DrinksInfo.m1chael888.Controllers;
+
+namespace DrinksInfo.m1chael888.Infrastructure;
+
+public interface IRouter
+{
+ void Route(DrinksController drinksController);
+}
+public class Router : IRouter
+{
+ public void Route(DrinksController drinksController)
+ {
+ while (true)
+ {
+ drinksController.HandleMainMenu();
+ }
+ }
+}
diff --git a/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Models/CategoryModel.cs b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Models/CategoryModel.cs
new file mode 100644
index 00000000..19595b7f
--- /dev/null
+++ b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Models/CategoryModel.cs
@@ -0,0 +1,13 @@
+using Newtonsoft.Json;
+
+namespace DrinksInfo.m1chael888.Models;
+
+public class Category
+{
+ public string strCategory { get; set; } = string.Empty;
+}
+public class Categories
+{
+ [JsonProperty("drinks")]
+ public List CategoriesList { get; set; }
+}
\ No newline at end of file
diff --git a/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Models/DrinkModel.cs b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Models/DrinkModel.cs
new file mode 100644
index 00000000..39b44687
--- /dev/null
+++ b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Models/DrinkModel.cs
@@ -0,0 +1,16 @@
+using Newtonsoft.Json;
+
+namespace DrinksInfo.m1chael888.Models;
+
+public class Drink
+{
+ public string strDrink { get; set; } = string.Empty;
+ public int idDrink { get; set; }
+ public string strDrinkThumb { get; set; } = string.Empty;
+}
+
+public class Drinks
+{
+ [JsonProperty("drinks")]
+ public List DrinkList { get; set; }
+}
diff --git a/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Program.cs b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Program.cs
new file mode 100644
index 00000000..6241067a
--- /dev/null
+++ b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Program.cs
@@ -0,0 +1,29 @@
+using Microsoft.Extensions.DependencyInjection;
+using DrinksInfo.m1chael888.Infrastructure;
+using DrinksInfo.m1chael888.Services;
+using DrinksInfo.m1chael888.Views;
+using System.Text;
+using DrinksInfo.m1chael888.Controllers;
+
+namespace DrinksInfo.m1chael888;
+
+internal class Program
+{
+ static void Main(string[] args)
+ {
+ Console.OutputEncoding = Encoding.UTF8;
+
+ var collection = new ServiceCollection();
+
+ collection.AddScoped();
+ collection.AddScoped();
+ collection.AddScoped();
+ collection.AddScoped ();
+
+ var provider = collection.BuildServiceProvider();
+
+ var drinksController = provider.GetRequiredService();
+ var router = provider.GetRequiredService();
+ router.Route(drinksController);
+ }
+}
\ No newline at end of file
diff --git a/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Services/DrinksService.cs b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Services/DrinksService.cs
new file mode 100644
index 00000000..cc19772c
--- /dev/null
+++ b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Services/DrinksService.cs
@@ -0,0 +1,50 @@
+using Newtonsoft.Json;
+using RestSharp;
+using DrinksInfo.m1chael888.Models;
+
+namespace DrinksInfo.m1chael888.Services;
+
+public interface IDrinksService
+{
+ List GetCategories();
+ List GetDrinks(string category);
+}
+public class DrinksService : IDrinksService
+{
+ private readonly string _drinkClient = "https://www.thecocktaildb.com/api/json/v1/1/";
+ public List GetCategories()
+ {
+ var client = new RestClient(_drinkClient);
+ var request = new RestRequest("list.php?c=list");
+ var response = client.ExecuteAsync(request);
+
+ if (response.Result.StatusCode == System.Net.HttpStatusCode.OK)
+ {
+ var responseString = response.Result.Content;
+ var serialize = JsonConvert.DeserializeObject(responseString);
+
+ List categories = serialize.CategoriesList;
+
+ return categories;
+ }
+ return new List { };
+ }
+
+ public List GetDrinks(string category)
+ {
+ var client = new RestClient(_drinkClient);
+ var request = new RestRequest($"filter.php?c={category}");
+ var response = client.ExecuteAsync(request);
+
+ if (response.Result.StatusCode == System.Net.HttpStatusCode.OK)
+ {
+ var responseString = response.Result.Content;
+ var serialize = JsonConvert.DeserializeObject(responseString);
+
+ List drinks = serialize.DrinkList;
+
+ return drinks;
+ }
+ return new List { };
+ }
+}
\ No newline at end of file
diff --git a/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Views/DrinksView.cs b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Views/DrinksView.cs
new file mode 100644
index 00000000..e4c1a782
--- /dev/null
+++ b/DrinksInfo.m1chael888/DrinksInfo.m1chael888/Views/DrinksView.cs
@@ -0,0 +1,83 @@
+using Spectre.Console;
+using DrinksInfo.m1chael888.Models;
+using static DrinksInfo.m1chael888.Enums.Extensions;
+using static DrinksInfo.m1chael888.Enums.MainMenuEnums;
+
+namespace DrinksInfo.m1chael888.Views;
+
+public interface IDrinksView
+{
+ Category ShowCategoryPrompt(List categories);
+ Drink ShowDrinkPrompt(List drinks);
+ void ShowDrinkInfo(Drink drink);
+ MainMenuOption ShowMainMenu();
+ void ShowAccessError(string msg);
+}
+public class DrinksView : IDrinksView
+{
+ public MainMenuOption ShowMainMenu()
+ {
+ Console.Clear();
+ return AnsiConsole.Prompt(
+ new SelectionPrompt()
+ .Title("[cyan]Main Menu::[/]")
+ .AddChoices(Enum.GetValues())
+ .UseConverter(x => $"[grey74]{GetDescription(x)}[/]")
+ .HighlightStyle("DarkOrange")
+ .WrapAround());
+ }
+
+ public Category ShowCategoryPrompt(List categories)
+ {
+ Console.Clear();
+ return AnsiConsole.Prompt(
+ new SelectionPrompt()
+ .Title("[cyan]Choose a category to view::[/]")
+ .AddChoices(categories)
+ .UseConverter(x => $"[grey74]{x.strCategory}[/]")
+ .HighlightStyle("DarkOrange")
+ .PageSize(categories.Count)
+ .WrapAround());
+ }
+
+ public Drink ShowDrinkPrompt(List drinks)
+ {
+ Console.Clear();
+ return AnsiConsole.Prompt(
+ new SelectionPrompt()
+ .Title("[cyan]Choose a drink::[/]")
+ .AddChoices(drinks)
+ .UseConverter(x => $"[grey74]{x.strDrink}[/]")
+ .HighlightStyle("DarkOrange")
+ .PageSize(25)
+ .WrapAround());
+ }
+
+ public void ShowDrinkInfo(Drink drink)
+ {
+ Console.Clear();
+ AnsiConsole.MarkupLine($"[cyan]Drink info::[/]\n");
+ AnsiConsole.MarkupLine($" [darkorange]Name -[/] [grey74]{drink.strDrink}[/]");
+ AnsiConsole.MarkupLine($" [darkorange]Id -[/] [grey74]{drink.idDrink}[/]");
+ AnsiConsole.MarkupLine($"[darkorange]Image -[/] [grey74]{drink.strDrinkThumb}[/]\n");
+ ReturnToMenu();
+ }
+
+ public void ShowAccessError(string msg)
+ {
+ Console.Clear();
+ AnsiConsole.MarkupLine($"[cyan]Error: {msg}[/]");
+ ReturnToMenu();
+ }
+
+ private void ReturnToMenu()
+ {
+ AnsiConsole.Status()
+ .Spinner(Spinner.Known.Point)
+ .SpinnerStyle("grey74")
+ .Start("[grey74]Press any key to return to menu[/]", x =>
+ {
+ Console.ReadKey();
+ });
+ }
+}
\ No newline at end of file
diff --git a/DrinksInfo.m1chael888/README.md b/DrinksInfo.m1chael888/README.md
new file mode 100644
index 00000000..5c7db7cf
--- /dev/null
+++ b/DrinksInfo.m1chael888/README.md
@@ -0,0 +1,6 @@
+# DrinksInfo.m1chael888
+This is a simple program used to call a drinks menu API in a C# Console. API calls are done using RestSharp, and the results are displayed in the console using Spectre.
+
+# How it works
+* Starting the app will display a main menu, allowing you to view drink categories or exit the app. Choosing to view categories will display a list of all available drink categories. To choose a category, navigate up and down the list using your arrow keys and press enter to select one.
+* After choosing a category, a list of all matching drinks will be similarly displayed, and you can choose one in the same way. After choosing a drink, the details associated with it will be displayed, including the drinks name, id and thumbnail image url. You can then press any key to return to the menu.