diff --git a/.gitignore b/.gitignore
index 49976e11..47972c0c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -478,3 +478,5 @@ $RECYCLE.BIN/
*.lnk
/MathGame2
/CodingTracker.TomDonegan/TextFile1.txt
+
+*.db
\ No newline at end of file
diff --git a/CodeReviews.Console.CodingTracker.sln b/CodeReviews.Console.CodingTracker.sln
new file mode 100644
index 00000000..d53da901
--- /dev/null
+++ b/CodeReviews.Console.CodingTracker.sln
@@ -0,0 +1,24 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.2.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodingTracker.Kaylubr", "CodingTracker.Kaylubr\CodingTracker.Kaylubr.csproj", "{6AABB9C0-5449-298C-1EB0-59B25D4A6CAC}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {6AABB9C0-5449-298C-1EB0-59B25D4A6CAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6AABB9C0-5449-298C-1EB0-59B25D4A6CAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6AABB9C0-5449-298C-1EB0-59B25D4A6CAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6AABB9C0-5449-298C-1EB0-59B25D4A6CAC}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {67FA8CC2-65C3-4683-B605-8F131556E3AC}
+ EndGlobalSection
+EndGlobal
diff --git a/CodingTracker.Kaylubr/CodingTracker.Kaylubr.csproj b/CodingTracker.Kaylubr/CodingTracker.Kaylubr.csproj
new file mode 100644
index 00000000..de3bc337
--- /dev/null
+++ b/CodingTracker.Kaylubr/CodingTracker.Kaylubr.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
diff --git a/CodingTracker.Kaylubr/Controllers/CodingSessionController.cs b/CodingTracker.Kaylubr/Controllers/CodingSessionController.cs
new file mode 100644
index 00000000..66845893
--- /dev/null
+++ b/CodingTracker.Kaylubr/Controllers/CodingSessionController.cs
@@ -0,0 +1,76 @@
+using CodingTracker.Utils;
+
+namespace CodingTracker.Controllers;
+
+internal static class CodingTrackerController
+{
+ internal static void InsertSession()
+ {
+ var (startTime, endTime) = UserInput.GetStartAndEndTime();
+ string duration = UserInput.GetDuration(startTime, endTime);
+ Database.Insert(startTime, endTime, duration);
+ Helper.Pause("Successful Operation!", success: true);
+ }
+
+ internal static void LogAllRecords()
+ {
+ Helper.RenderCodingSessionInTable(Database.GetAll());
+ Helper.Pause();
+ }
+
+ internal static void UpdateRecord()
+ {
+ bool exists = Helper.RenderCodingSessionInTable(Database.GetAll());
+
+ if (exists)
+ {
+ int id = UserInput.GetID("EDITED");
+
+ if (!Database.FindOneSession(id))
+ {
+ Helper.Pause("Record not found!", success: false);
+ return;
+ }
+
+ var (startTime, endTime) = UserInput.GetStartAndEndTime();
+ string duration = UserInput.GetDuration(startTime, endTime);
+
+ Database.Update(id, startTime, endTime, duration);
+
+ Helper.Pause("Successful Operation!", success: true);
+ }
+ else
+ {
+ Helper.Pause();
+ }
+ }
+
+ internal static void DeleteRecord()
+ {
+ bool exists = Helper.RenderCodingSessionInTable(Database.GetAll());
+
+ if (exists)
+ {
+ int id = UserInput.GetID("DELETED");
+
+ if (!Database.FindOneSession(id))
+ {
+ Helper.Pause("Record not found!", success: false);
+ return;
+ }
+
+ if (!Helper.Confirmation("Are you sure?"))
+ {
+ return;
+ }
+
+ Database.DeleteOne(id);
+
+ Helper.Pause("Successful Operation!", success: true);
+ }
+ else
+ {
+ Helper.Pause();
+ }
+ }
+}
\ No newline at end of file
diff --git a/CodingTracker.Kaylubr/Enums/MenuChoices.cs b/CodingTracker.Kaylubr/Enums/MenuChoices.cs
new file mode 100644
index 00000000..a59f91fa
--- /dev/null
+++ b/CodingTracker.Kaylubr/Enums/MenuChoices.cs
@@ -0,0 +1,10 @@
+namespace CodingTracker.Enums;
+
+internal enum MenuChoices
+{
+ View,
+ Insert,
+ Update,
+ Delete,
+ Exit
+}
\ No newline at end of file
diff --git a/CodingTracker.Kaylubr/Models/CodingSession.cs b/CodingTracker.Kaylubr/Models/CodingSession.cs
new file mode 100644
index 00000000..95f00f66
--- /dev/null
+++ b/CodingTracker.Kaylubr/Models/CodingSession.cs
@@ -0,0 +1,9 @@
+namespace CodingTracker.Models;
+
+public class CodingSession
+{
+ public int Id { get; set; }
+ public DateTime StartTime { get; set; }
+ public DateTime EndTime { get; set; }
+ public string Duration { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/CodingTracker.Kaylubr/Program.cs b/CodingTracker.Kaylubr/Program.cs
new file mode 100644
index 00000000..9c42948a
--- /dev/null
+++ b/CodingTracker.Kaylubr/Program.cs
@@ -0,0 +1,5 @@
+using CodingTracker.Views;
+using CodingTracker.Utils;
+
+Database.CreateDatabase();
+UserInterface.Run();
\ No newline at end of file
diff --git a/CodingTracker.Kaylubr/Utils/Config.cs b/CodingTracker.Kaylubr/Utils/Config.cs
new file mode 100644
index 00000000..46774a10
--- /dev/null
+++ b/CodingTracker.Kaylubr/Utils/Config.cs
@@ -0,0 +1,16 @@
+using Microsoft.Extensions.Configuration;
+
+namespace CodingTracker.Utils;
+
+internal static class Config
+{
+ internal static string? InitializeConfig()
+ {
+ var configuration = new ConfigurationBuilder()
+ .SetBasePath(AppContext.BaseDirectory)
+ .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
+ .Build();
+
+ return configuration.GetConnectionString("DefaultConnection");
+ }
+}
\ No newline at end of file
diff --git a/CodingTracker.Kaylubr/Utils/Database.cs b/CodingTracker.Kaylubr/Utils/Database.cs
new file mode 100644
index 00000000..d17501ea
--- /dev/null
+++ b/CodingTracker.Kaylubr/Utils/Database.cs
@@ -0,0 +1,70 @@
+using CodingTracker.Models;
+using Dapper;
+using Microsoft.Data.Sqlite;
+
+namespace CodingTracker.Utils;
+
+internal static class Database
+{
+ readonly static string? connectionString = Config.InitializeConfig();
+ readonly static SqliteConnection connection = new(connectionString);
+
+ internal static void CreateDatabase()
+ {
+ var sql = @"CREATE TABLE IF NOT EXISTS coding_session (
+ Id INTEGER PRIMARY KEY AUTOINCREMENT,
+ StartTime TEXT,
+ EndTime TEXT,
+ Duration TEXT
+ )";
+
+ connection.Execute(sql);
+ }
+
+ internal static void Insert(string st, string et, string duration)
+ {
+ var sql = @"INSERT INTO coding_session (StartTime, EndTime, Duration)
+ VALUES (@start, @end, @duration)
+ ";
+
+ connection.Execute(sql, new { start = st, end = et, duration });
+ }
+
+ internal static List GetAll()
+ {
+ var sql = "SELECT * FROM coding_session";
+ List records = connection.Query(sql).ToList();
+
+ return records;
+ }
+
+ internal static void Update(int id, string st, string et, string duration)
+ {
+ var sql = "UPDATE coding_session SET StartTime = @st, EndTime = @et, Duration = @duration WHERE Id = @id";
+ var obj = new { id, st, et, duration };
+
+ connection.Execute(sql, obj);
+ }
+
+ internal static void DeleteOne(int id)
+ {
+ var sql = "DELETE FROM coding_session WHERE id = @id";
+ connection.Execute(sql, new { id });
+ }
+
+ internal static bool FindOneSession(int id)
+ {
+ var sql = "SELECT * FROM coding_session WHERE Id = @id";
+ var obj = new { id };
+
+ try
+ {
+ connection.QuerySingle(sql, obj);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/CodingTracker.Kaylubr/Utils/Helper.cs b/CodingTracker.Kaylubr/Utils/Helper.cs
new file mode 100644
index 00000000..bda8863b
--- /dev/null
+++ b/CodingTracker.Kaylubr/Utils/Helper.cs
@@ -0,0 +1,89 @@
+using CodingTracker.Models;
+using Spectre.Console;
+
+namespace CodingTracker.Utils;
+
+internal static class Helper
+{
+ internal static bool ValidateTime(DateTime start, DateTime end)
+ {
+ if (start > end)
+ {
+ AnsiConsole.MarkupLine("[red]Starting time shouldn't be greater than the end time.[/]");
+ return false;
+ }
+
+ return true;
+ }
+
+ internal static bool RenderCodingSessionInTable(List codingSessions)
+ {
+ AnsiConsole.Clear();
+
+ if (codingSessions.Count >= 1)
+ {
+ var table = new Table()
+ .Title("[green bold]Session Records[/]")
+ .Border(TableBorder.Heavy);
+
+ table.AddColumn("ID");
+ table.AddColumn("Starting time");
+ table.AddColumn("End time");
+ table.AddColumn("Duration");
+
+ foreach (var session in codingSessions)
+ {
+ table.AddRow(session.Id.ToString(), session.StartTime.ToString(), session.EndTime.ToString(), session.Duration);
+ }
+
+ AnsiConsole.Write(table);
+
+ return true;
+ }
+ else
+ {
+ AnsiConsole.MarkupLine("[red]No coding sessions to display. Add a session to see it here.[/]");
+ return false;
+ }
+
+ }
+
+ internal static bool Confirmation(string message)
+ {
+ AnsiConsole.WriteLine();
+
+ string? choice;
+ do
+ {
+ choice = AnsiConsole.Ask($"{message} [bold green]Y[/] or [bold red]N[/]:").Trim().ToUpper();
+ } while (choice != "Y" && choice != "N");
+
+ if (choice == "N")
+ return false;
+
+ return true;
+ }
+
+ internal static void Pause()
+ {
+ AnsiConsole.Write("\nPress any key to continue..");
+ Console.ReadKey();
+ }
+
+ internal static void Pause(string message, bool success)
+ {
+ if (success)
+ {
+ AnsiConsole.MarkupLine($"\n[green]{message}[/]");
+ }
+ else
+ {
+ AnsiConsole.MarkupLine($"\n[red]{message}[/]");
+ }
+
+ AnsiConsole.Write("\nPress any key to continue..");
+ Console.ReadKey();
+
+ AnsiConsole.Clear();
+ }
+}
diff --git a/CodingTracker.Kaylubr/Utils/UserInput.cs b/CodingTracker.Kaylubr/Utils/UserInput.cs
new file mode 100644
index 00000000..20f0eca1
--- /dev/null
+++ b/CodingTracker.Kaylubr/Utils/UserInput.cs
@@ -0,0 +1,56 @@
+using Spectre.Console;
+using System.Globalization;
+
+namespace CodingTracker.Utils;
+
+internal static class UserInput
+{
+ internal static int GetID(string mode)
+ {
+ AnsiConsole.WriteLine();
+ return AnsiConsole.Ask($"\nEnter the [green]ID[/] of the row to be [bold]{mode}[/]: ");
+ }
+
+ internal static (string StartTime, string EndTime) GetStartAndEndTime()
+ {
+ DateTime start;
+ DateTime end;
+
+ do
+ {
+ AnsiConsole.WriteLine();
+ start = GetTime("START");
+ end = GetTime("END");
+ } while (!Helper.ValidateTime(start, end));
+
+ string startTime = start.ToString();
+ string endTime = end.ToString();
+
+ return (startTime, endTime);
+ }
+
+ static DateTime GetTime(string message)
+ {
+ while (true)
+ {
+ var time = AnsiConsole.Ask($"Enter [bold green]{message}[/] session time in the format (dd-MM-yy HH-mm): ");
+
+ if (DateTime.TryParseExact(time, "dd-MM-yy HH:mm", new CultureInfo("en-US"), DateTimeStyles.None, out _))
+ {
+ return DateTime.ParseExact(time, "dd-MM-yy HH:mm", new CultureInfo("en-US"));
+ }
+
+ AnsiConsole.MarkupLine("[red]Invalid Format or Invalid Date & Time[/]");
+ }
+
+ }
+
+ internal static string GetDuration(string start, string end)
+ {
+ DateTime startTime = DateTime.Parse(start);
+ DateTime endTime = DateTime.Parse(end);
+
+ return (endTime - startTime).ToString(@"hh\:mm\:ss");
+ }
+
+}
\ No newline at end of file
diff --git a/CodingTracker.Kaylubr/Views/UserInterface.cs b/CodingTracker.Kaylubr/Views/UserInterface.cs
new file mode 100644
index 00000000..dfe024b8
--- /dev/null
+++ b/CodingTracker.Kaylubr/Views/UserInterface.cs
@@ -0,0 +1,67 @@
+using Spectre.Console;
+using CodingTracker.Controllers;
+using CodingTracker.Enums;
+using CodingTracker.Utils;
+
+namespace CodingTracker.Views;
+
+internal static class UserInterface
+{
+ internal static void Run()
+ {
+ while (true)
+ {
+ RenderTitle();
+
+ var choice = AnsiConsole.Prompt(
+ new SelectionPrompt()
+ .Title("\n[Green]Pick operation:[/]")
+ .HighlightStyle(new Style(Color.Green))
+ .AddChoices(Enum.GetValues())
+ );
+
+ switch (choice)
+ {
+ case MenuChoices.View:
+ CodingTrackerController.LogAllRecords();
+ break;
+
+ case MenuChoices.Insert:
+
+ if (!Helper.Confirmation("Proceed with this operation? (No will return to the main menu)"))
+ continue;
+
+ CodingTrackerController.InsertSession();
+ break;
+
+ case MenuChoices.Update:
+
+ if (!Helper.Confirmation("Proceed with this operation? (No will return to the main menu)"))
+ continue;
+
+ CodingTrackerController.UpdateRecord();
+ break;
+
+ case MenuChoices.Delete:
+
+ if (!Helper.Confirmation("Proceed with this operation? (No will return to the main menu)"))
+ continue;
+
+ CodingTrackerController.DeleteRecord();
+ break;
+ case MenuChoices.Exit:
+ AnsiConsole.WriteLine("\nExiting..");
+ Environment.Exit(0);
+ break;
+ }
+ }
+ }
+
+ static void RenderTitle()
+ {
+ Console.Clear();
+ var panel = new Panel("Welcome to Coding Session Tracker!");
+ panel.Border(BoxBorder.Heavy);
+ AnsiConsole.Write(panel);
+ }
+}
diff --git a/CodingTracker.Kaylubr/appsettings.json b/CodingTracker.Kaylubr/appsettings.json
new file mode 100644
index 00000000..be023eb9
--- /dev/null
+++ b/CodingTracker.Kaylubr/appsettings.json
@@ -0,0 +1,5 @@
+{
+ "ConnectionStrings": {
+ "DefaultConnection": "Data Source=HabitTracker.db"
+ }
+}
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..ea30f115
--- /dev/null
+++ b/README.md
@@ -0,0 +1,42 @@
+# Coding Tracker Console App
+
+A simple console-based coding session tracker built with **.NET**, **Dapper**, **Microsoft.Extensions.Configuration**, **Microsoft.Data.Sqlite**, and **Spectre.Console**. The app allows you to **view, add, edit, and delete** coding sessions in a lightweight SQLite database with a clean console interface.
+I like this project because I feel like I had better grasp on OOP and patterns like DRY and KISS
+
+---
+
+## Features
+
+- Add new coding sessions with start and end times
+- View all recorded sessions in a formatted console table
+- Edit existing sessions directly from the console
+- Delete sessions when needed
+- Uses **Spectre.Console** for a colorful and user-friendly terminal UI
+- Configuration handled via **Microsoft.Extensions.Configuration** for flexible database and app settings
+
+---
+
+## Challenges
+
+One thing that really frustrated me was how **Dapper maps values** from the database to C# objects.
+
+At first, I named my columns `start_time` and `end_time`. When I tried to render them, Dapper didn’t automatically convert them into `DateTime` even though the types were declared in `models/CodingSession.cs`. The issue was that Dapper maps columns to properties **by name**, and I was sticking to C#’s PascalCase naming convention instead of the database’s snake_case convention.
+
+The fix was simple: rename the columns to `StartTime` and `EndTime` to match the property names in my model. Once that was done, Dapper handled the mapping perfectly.
+
+---
+
+## Lessons Learned
+
+- **Dapper is incredibly convenient**. You don’t need to manually open or close the database connection, and retrieving rows into a list is simple using Dapper’s built-in methods
+- Keeping column names and model property names consistent is crucial for smooth mapping
+- **Spectre.Console** makes even simple console apps feel interactive and polished
+
+---
+
+## Resources Used
+
+- [Dapper Documentation](https://dapper-tutorial.net/)
+- [Microsoft.Data.Sqlite](https://learn.microsoft.com/en-us/dotnet/standard/data/sqlite/)
+- [Microsoft.Extensions.Configuration](https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration)
+- [Spectre.Console](https://spectreconsole.net/)