diff --git a/.gitignore b/.gitignore
index 49976e11..94e93c97 100644
--- a/.gitignore
+++ b/.gitignore
@@ -478,3 +478,6 @@ $RECYCLE.BIN/
*.lnk
/MathGame2
/CodingTracker.TomDonegan/TextFile1.txt
+
+# DB
+*.db
\ No newline at end of file
diff --git a/Tenebris-06.CodingTracker/CodingTracker.csproj b/Tenebris-06.CodingTracker/CodingTracker.csproj
new file mode 100644
index 00000000..12273964
--- /dev/null
+++ b/Tenebris-06.CodingTracker/CodingTracker.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/Tenebris-06.CodingTracker/Data/DataAccess.cs b/Tenebris-06.CodingTracker/Data/DataAccess.cs
new file mode 100644
index 00000000..dccc7e11
--- /dev/null
+++ b/Tenebris-06.CodingTracker/Data/DataAccess.cs
@@ -0,0 +1,72 @@
+using System.Collections.Immutable;
+using Microsoft.Data.Sqlite;
+using Dapper;
+
+public class DataAccess
+{
+ string _ConnectionString;
+
+ public DataAccess(string Connectionstring)
+ {
+ _ConnectionString = Connectionstring;
+ }
+
+ public void Initialize()
+ {
+ using var connection = new SqliteConnection(_ConnectionString);
+ connection.Open();
+
+ connection.Execute("""
+ CREATE TABLE IF NOT EXISTS CodingSessions(
+ Id INTEGER PRIMARY KEY AUTOINCREMENT,
+ StartTime TEXT NOT NULL,
+ EndTime TEXT NOT NULL,
+ Duration TEXT NOT NULL,
+ Description TEXT)
+ """);
+ }
+
+ public void CreateSession(Session session)
+ {
+ var sql = """
+ INSERT INTO CodingSessions (StartTime, EndTime, Duration, Description)
+ VALUES (@StartTime, @EndTime, @Duration, @Description)
+ """;
+ using var connection = new SqliteConnection(_ConnectionString);
+ connection.Execute(sql, session);
+ }
+
+ public void DeleteSession(int SessionId)
+ {
+ var sql = """
+ DELETE FROM CodingSessions WHERE Id = @Id
+ """;
+ using var connection = new SqliteConnection(_ConnectionString);
+ connection.Execute(sql, new {Id = SessionId});
+ }
+
+ public void UpdateSession(Session session)
+ {
+ var sql = """
+ UPDATE CodingSessions
+ SET StartTime = @StartTime, EndTime = @EndTime,
+ Duration = @Duration, Description = @Description
+ WHERE
+ Id = @Id
+ """;
+ using var connection = new SqliteConnection(_ConnectionString);
+ connection.Execute(sql, session);
+ }
+
+ public List ReadSessions()
+ {
+ var sql = """
+ SELECT * FROM CodingSessions
+ """;
+ using var connection = new SqliteConnection(_ConnectionString);
+
+ var sessions = connection.Query(sql);
+
+ return sessions.ToList();
+ }
+}
\ No newline at end of file
diff --git a/Tenebris-06.CodingTracker/Helpers/DateTimeHelper.cs b/Tenebris-06.CodingTracker/Helpers/DateTimeHelper.cs
new file mode 100644
index 00000000..6f36cf25
--- /dev/null
+++ b/Tenebris-06.CodingTracker/Helpers/DateTimeHelper.cs
@@ -0,0 +1,23 @@
+using System.Globalization;
+using System.Security.Cryptography.X509Certificates;
+using Spectre.Console;
+
+public static class DateTimeHelper{
+ public static bool TryGetDateTime(string input, out DateTime result)
+{
+ if (string.IsNullOrWhiteSpace(input))
+ {
+ result = DateTime.Now;
+ return true;
+ }
+
+ return DateTime.TryParseExact(
+ input,
+ "yyyy-MM-dd HH:mm:ss",
+ CultureInfo.InvariantCulture,
+ DateTimeStyles.None,
+ out result
+ );
+}
+
+}
\ No newline at end of file
diff --git a/Tenebris-06.CodingTracker/Helpers/TimeSpanHandler.cs b/Tenebris-06.CodingTracker/Helpers/TimeSpanHandler.cs
new file mode 100644
index 00000000..adc68938
--- /dev/null
+++ b/Tenebris-06.CodingTracker/Helpers/TimeSpanHandler.cs
@@ -0,0 +1,15 @@
+using System.Data;
+using Dapper;
+
+public class TimeSpanHandler : SqlMapper.TypeHandler
+{
+ public override void SetValue(IDbDataParameter parameter, TimeSpan value)
+ {
+ parameter.Value = value.ToString(@"hh\:mm\:ss");
+ }
+
+ public override TimeSpan Parse(object value)
+ {
+ return TimeSpan.Parse(value.ToString()!);
+ }
+}
\ No newline at end of file
diff --git a/Tenebris-06.CodingTracker/Models/Session.cs b/Tenebris-06.CodingTracker/Models/Session.cs
new file mode 100644
index 00000000..bd81005b
--- /dev/null
+++ b/Tenebris-06.CodingTracker/Models/Session.cs
@@ -0,0 +1,8 @@
+public class Session
+{
+ public int Id { get; set; }
+ public DateTime StartTime { get; set; }
+ public DateTime EndTime { get; set; }
+ public TimeSpan Duration { get; set; }
+ public string? Description { get; set; }
+}
\ No newline at end of file
diff --git a/Tenebris-06.CodingTracker/Program.cs b/Tenebris-06.CodingTracker/Program.cs
new file mode 100644
index 00000000..e4795dc9
--- /dev/null
+++ b/Tenebris-06.CodingTracker/Program.cs
@@ -0,0 +1,24 @@
+using System;
+using Dapper;
+using Microsoft.Extensions.Configuration;
+using Spectre.Console;
+
+class Program {
+ static void Main(string[] args)
+ {
+
+ SqlMapper.AddTypeHandler(new TimeSpanHandler());
+
+ var configuration = new ConfigurationBuilder()
+ .SetBasePath(AppContext.BaseDirectory)
+ .AddJsonFile("appsettings.json")
+ .Build();
+
+ DataAccess db = new DataAccess(configuration["Database:ConnectionString"]);
+ db.Initialize();
+
+ var UI = new Menu(db);
+ UI.MainMenu();
+
+ }
+}
\ No newline at end of file
diff --git a/Tenebris-06.CodingTracker/Services/SessionService.cs b/Tenebris-06.CodingTracker/Services/SessionService.cs
new file mode 100644
index 00000000..e44159a5
--- /dev/null
+++ b/Tenebris-06.CodingTracker/Services/SessionService.cs
@@ -0,0 +1,24 @@
+public class SessionService
+{
+ public DateTime startTime;
+ public void StartSession()
+ {
+ startTime = DateTime.Now;
+ }
+
+ public Session EndSession()
+ {
+ DateTime endTime = DateTime.Now;
+ return new Session
+ {
+ StartTime = startTime,
+ EndTime = endTime,
+ Duration = endTime - startTime
+ };
+ }
+
+ public TimeSpan GetElapsedTime()
+ {
+ return DateTime.Now - startTime;
+ }
+}
\ No newline at end of file
diff --git a/Tenebris-06.CodingTracker/UI/Menu.cs b/Tenebris-06.CodingTracker/UI/Menu.cs
new file mode 100644
index 00000000..a7bba1f6
--- /dev/null
+++ b/Tenebris-06.CodingTracker/UI/Menu.cs
@@ -0,0 +1,238 @@
+using System.Formats.Asn1;
+using Spectre.Console;
+public class Menu
+{
+ private readonly DataAccess _db;
+ public Menu(DataAccess db)
+ {
+ _db = db;
+ }
+ public void MainMenu()
+ {
+
+ while (true)
+ {
+ Console.Clear();
+ // AnsiConsole.MarkupLine("[green] Coding Tracker[/]").Centered();
+
+ AnsiConsole.Write(
+ new Align(
+ new Markup("[green]Coding Tracker[/]"),
+ HorizontalAlignment.Center
+ )
+ );
+
+
+ var choice = AnsiConsole.Prompt(
+ new SelectionPrompt()
+ .Title("Select an option: ")
+ .AddChoices("Add a Session", "Delete a Session", "View All Sessions",
+ "Start a Session with a Timer")
+ );
+
+ switch (choice)
+ {
+ case "Add a Session":
+ AddSessionMenu();
+ break;
+
+ case "Delete a Session":
+ DeleteSessionMenu();
+ break;
+
+ case "View All Sessions":
+ ViewSessionsMenu();
+ break;
+
+ case "Start a Session with a Timer":
+ StartSessionMenu();
+ break;
+
+ default:
+ AnsiConsole.MarkupLine("[yellow] Please choose an option[/]");
+ continue;
+ }
+ }
+ }
+
+ public void AddSessionMenu()
+ {
+ Console.Clear();
+ while (true)
+ {
+ DateTime startTime;
+ DateTime endTime;
+
+ AnsiConsole.MarkupLine("Enter Dates and Times in this format [green]yyyy-MM-dd HH:mm:ss[/]");
+ AnsiConsole.MarkupLine("[yellow] Or leave empty to enter the current datetime! [/]");
+
+ while (true)
+ {
+ string input = AnsiConsole.Prompt(
+ new TextPrompt("Enter the start time:")
+ .AllowEmpty()
+ );
+
+ if (DateTimeHelper.TryGetDateTime(input, out startTime))
+ break;
+
+ AnsiConsole.MarkupLine(
+ "[red]Invalid input, please try again.[/]"
+ );
+ }
+
+ while (true)
+ {
+ string input = AnsiConsole.Prompt(
+ new TextPrompt("Enter the end time:")
+ .AllowEmpty()
+ );
+
+ if (DateTimeHelper.TryGetDateTime(input, out endTime))
+ break;
+
+ AnsiConsole.MarkupLine(
+ "[red]Invalid input, please try again.[/]"
+ );
+ }
+
+ TimeSpan duration = endTime - startTime;
+ string description = AnsiConsole.Prompt(
+ new TextPrompt("Enter a Description or a Note [yellow](optional)[/]")
+ .AllowEmpty()
+ );
+
+ if (AnsiConsole.Confirm("Add Session?"))
+ {
+ _db.CreateSession(new Session { StartTime = startTime, EndTime = endTime,
+ Duration = duration, Description = description});
+
+ AnsiConsole.MarkupLine("[green]Session Added[/]");
+
+ break;
+ } else
+ {
+ break;
+ }
+ }
+
+
+
+ }
+
+ public void DeleteSessionMenu()
+ {
+ Console.Clear();
+
+ List sessions = _db.ReadSessions();
+ AnsiConsole.MarkupLine("Enter the [blue]ID[/] of the session you would like to delete:");
+ DisplayTable(sessions);
+
+ while (true)
+ {
+ int IdToDelete = AnsiConsole.Ask("ID:");
+
+
+ if (sessions.Any(s => s.Id == IdToDelete)
+ && AnsiConsole.Confirm("Delete session?")
+ )
+ {
+ _db.DeleteSession(IdToDelete);
+ AnsiConsole.MarkupLine("[green]Session Deleted[/]");
+ break;
+ } else
+ {
+ AnsiConsole.MarkupLine("[red]Session does not exist, please try again.[/]");
+ continue;
+ }
+ }
+
+ }
+
+ public void ViewSessionsMenu()
+ {
+ Console.Clear();
+
+ List sessions = _db.ReadSessions();
+ DisplayTable(sessions);
+ AnsiConsole.MarkupLine("Press [blue]ESC[/] to go back");
+
+ while (Console.ReadKey(true).Key != ConsoleKey.Escape)
+ {
+
+ }
+ }
+
+ public void StartSessionMenu()
+ {
+ Console.Clear();
+ SessionService stopWatchData = new SessionService();
+ stopWatchData.StartSession();
+ while (true)
+ {
+ Console.Clear();
+ var stopWatch = new Panel(
+ new Rows(
+ new Markup(" "),
+ new Markup($"[bold green]{stopWatchData.GetElapsedTime()
+ .ToString(@"hh\:mm\:ss")}[/]").Centered(),
+ new Markup(" "),
+ new Markup("[yellow]Press ESC to stop[/]").Centered()
+ )
+ )
+ .Header("[bold blue] Live Session [/]", Justify.Center)
+ .DoubleBorder()
+ .Expand();
+
+ AnsiConsole.Write(stopWatch);
+
+ if (Console.KeyAvailable)
+ {
+ var key = Console.ReadKey(true);
+
+ if (key.Key == ConsoleKey.Escape)
+ {
+ if (AnsiConsole.Confirm("[bold] Add this session? [/]"))
+ {
+ string description = AnsiConsole.Prompt(
+ new TextPrompt("Enter a Description or a Note [yellow](optional)[/]")
+ .AllowEmpty()
+ );
+ Session s = stopWatchData.EndSession();
+ s.Description = description;
+ _db.CreateSession(s);
+ AnsiConsole.MarkupLine("[bold green] Session Created![/]");
+ }
+ Console.Clear();
+ break;
+ }
+ }
+
+ Thread.Sleep(1000);
+
+ }
+ }
+ public void DisplayTable(List list)
+ {
+ var table = new Table()
+ .Title("[green]Session List[/]");
+
+ table.AddColumn("ID")
+ .AddColumn("Start Time")
+ .AddColumn("End Time")
+ .AddColumn("Duration")
+ .AddColumn("Description");
+
+ foreach (var v in list)
+ {
+ table.AddRow(
+ v.Id.ToString(),
+ v.StartTime.ToString("yyyy-MM-dd HH:mm:ss"),
+ v.EndTime.ToString("yyyy-MM-dd HH:mm:ss"),
+ v.Duration.ToString(@"hh\:mm\:ss"),
+ v.Description ?? ""
+ );
+ }
+ AnsiConsole.Write(table);
+ }
+}
\ No newline at end of file
diff --git a/Tenebris-06.CodingTracker/appsettings.json b/Tenebris-06.CodingTracker/appsettings.json
new file mode 100644
index 00000000..8d3e2aff
--- /dev/null
+++ b/Tenebris-06.CodingTracker/appsettings.json
@@ -0,0 +1,6 @@
+{
+ "AppName": "Coding Tracker",
+ "Database": {
+ "ConnectionString": "Data Source=app.db"
+ }
+}
\ No newline at end of file