From af409f39d972dfbd173bf107dadd6008820c6493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kurti=C4=87=20D=C5=BEemal?= Date: Mon, 7 Sep 2026 09:37:06 +0200 Subject: [PATCH 1/5] Add CodingSession model --- .../CodingTracker.DzemalKurtic.slnx | 3 +++ .../CodingTracker.DzemalKurtic.csproj | 22 +++++++++++++++++++ .../Models/CodingSession.cs | 21 ++++++++++++++++++ .../CodingTracker.DzemalKurtic/Program.cs | 7 ++++++ .../Properties/launchSettings.json | 8 +++++++ .../appsettings.json | 5 +++++ 6 files changed, 66 insertions(+) create mode 100644 CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic.slnx create mode 100644 CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic.csproj create mode 100644 CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Models/CodingSession.cs create mode 100644 CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Program.cs create mode 100644 CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Properties/launchSettings.json create mode 100644 CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/appsettings.json diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic.slnx b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic.slnx new file mode 100644 index 00000000..bbdb9534 --- /dev/null +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic.slnx @@ -0,0 +1,3 @@ + + + diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic.csproj b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic.csproj new file mode 100644 index 00000000..f4baa05c --- /dev/null +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic.csproj @@ -0,0 +1,22 @@ + + + + Exe + net10.0 + enable + enable + + + + + + + + + + + PreserveNewest + + + + diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Models/CodingSession.cs b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Models/CodingSession.cs new file mode 100644 index 00000000..074e62cc --- /dev/null +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Models/CodingSession.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CodingTracker.DzemalKurtic.Models +{ + internal class CodingSession + { + public int Id { get; set; } + public DateTime StartTime { get; set; } + public DateTime EndTime { get; set; } + + private TimeSpan Duration => EndTime - StartTime; + public CodingSession(int id, DateTime startTime, DateTime endTime) + { + Id = id; + StartTime = startTime; + EndTime = endTime; + } + } +} diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Program.cs b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Program.cs new file mode 100644 index 00000000..41ae01de --- /dev/null +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Program.cs @@ -0,0 +1,7 @@ +using Microsoft.Extensions.Configuration; + +IConfiguration config = new ConfigurationBuilder() + .AddJsonFile("appsettings.json") + .Build(); + +var connectionString = config.GetConnectionString("conectionString"); \ No newline at end of file diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Properties/launchSettings.json b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Properties/launchSettings.json new file mode 100644 index 00000000..7a805b4d --- /dev/null +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "CodingTracker.DzemalKurtic": { + "commandName": "Project", + "workingDirectory": "C:\\Users\\User\\Desktop\\CSharpAcademy\\CodeReviews.Console.HabitTracker\\CodingTracker.DzemalKurtic\\" + } + } +} \ No newline at end of file diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/appsettings.json b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/appsettings.json new file mode 100644 index 00000000..03a17ca3 --- /dev/null +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/appsettings.json @@ -0,0 +1,5 @@ +{ + "ConnectionStrings": { + "connectionString" : "Data Source=coding-Tracker.db" + } +} \ No newline at end of file From 66f4e2fb2f7313b7a120a0e55a9a1e41ec29b923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kurti=C4=87=20D=C5=BEemal?= Date: Mon, 7 Sep 2026 10:07:41 +0200 Subject: [PATCH 2/5] Add database --- .../CodingTracker.DzemalKurtic.csproj | 2 +- .../Data/Database.cs | 26 +++++++++++++++++++ .../CodingTracker.DzemalKurtic/Program.cs | 7 +++-- .../Properties/launchSettings.json | 2 +- .../appsettings.json | 2 +- 5 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Data/Database.cs diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic.csproj b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic.csproj index f4baa05c..70fd0482 100644 --- a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic.csproj +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic.csproj @@ -9,7 +9,7 @@ - + diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Data/Database.cs b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Data/Database.cs new file mode 100644 index 00000000..8ec2306d --- /dev/null +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Data/Database.cs @@ -0,0 +1,26 @@ +using Dapper; +using Microsoft.Data.Sqlite; + +namespace CodingTracker.DzemalKurtic.Data +{ + internal static class Database + { + public static void Initialize(string connectionString) + { + using var connection = new SqliteConnection(connectionString); + + connection.Open(); + + var sql = + """ + CREATE TABLE IF NOT EXISTS coding_sessions ( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + StartTime TEXT NOT NULL, + EndTime TEXT NOT NULL + ); + """; + + connection.Execute(sql ); + } + } +} diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Program.cs b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Program.cs index 41ae01de..fbe57d69 100644 --- a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Program.cs +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Program.cs @@ -1,7 +1,10 @@ -using Microsoft.Extensions.Configuration; +using CodingTracker.DzemalKurtic.Data; +using Microsoft.Extensions.Configuration; IConfiguration config = new ConfigurationBuilder() .AddJsonFile("appsettings.json") .Build(); -var connectionString = config.GetConnectionString("conectionString"); \ No newline at end of file +var connectionString = config.GetConnectionString("DefaultConnection"); + +Database.Initialize(connectionString); \ No newline at end of file diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Properties/launchSettings.json b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Properties/launchSettings.json index 7a805b4d..acc35f31 100644 --- a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Properties/launchSettings.json +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "CodingTracker.DzemalKurtic": { "commandName": "Project", - "workingDirectory": "C:\\Users\\User\\Desktop\\CSharpAcademy\\CodeReviews.Console.HabitTracker\\CodingTracker.DzemalKurtic\\" + "workingDirectory": "C:\\Users\\User\\Desktop\\CSharpAcademy\\CodeReviews.Console.CodingTracker\\CodingTracker.DzemalKurtic\\CodingTracker.DzemalKurtic\\" } } } \ No newline at end of file diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/appsettings.json b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/appsettings.json index 03a17ca3..04d1f510 100644 --- a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/appsettings.json +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/appsettings.json @@ -1,5 +1,5 @@ { "ConnectionStrings": { - "connectionString" : "Data Source=coding-Tracker.db" + "DefaultConnection" : "Data Source=coding-Tracker.db" } } \ No newline at end of file From a6908f5c4afa6f8d503d85556528b2f9bd5df5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kurti=C4=87=20D=C5=BEemal?= Date: Mon, 7 Sep 2026 16:53:27 +0200 Subject: [PATCH 3/5] Add CRUD --- .../CodingTracker.DzemalKurtic.csproj | 1 + .../Controllers/CodingSessionController.cs | 72 +++++++++++ .../Data/Database.cs | 32 +++-- .../CodingTracker.DzemalKurtic/MenuAction.cs | 12 ++ .../Models/CodingSession.cs | 35 +++-- .../CodingTracker.DzemalKurtic/Program.cs | 9 +- .../Properties/launchSettings.json | 3 +- .../Views/UserInterface.cs | 120 ++++++++++++++++++ 8 files changed, 251 insertions(+), 33 deletions(-) create mode 100644 CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Controllers/CodingSessionController.cs create mode 100644 CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/MenuAction.cs create mode 100644 CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Views/UserInterface.cs diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic.csproj b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic.csproj index 70fd0482..2aaf3bf6 100644 --- a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic.csproj +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic.csproj @@ -11,6 +11,7 @@ + diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Controllers/CodingSessionController.cs b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Controllers/CodingSessionController.cs new file mode 100644 index 00000000..c3796f81 --- /dev/null +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Controllers/CodingSessionController.cs @@ -0,0 +1,72 @@ +using CodingTracker.DzemalKurtic.Models; +using CodingTracker.DzemalKurtic.Views; +using Dapper; +using Microsoft.Data.Sqlite; + +namespace CodingTracker.DzemalKurtic.Controllers; + +internal class CodingSessionController +{ + public string ConnectionString { get; set; } + public CodingSessionController(string connectionString) + { + ConnectionString = connectionString; + } + + public void AddItem(DateTime start, DateTime end) + { + using var connection = new SqliteConnection(ConnectionString); + connection.Open(); + + var sql = + """ + INSERT INTO coding_sessions + (StartTime, EndTime) + VALUES (@StartTime, @EndTime) + """; + + var session = new CodingSession { StartTime = start, EndTime = end }; + + connection.Execute(sql, session); + } + + public void DeleteItem(int id) + { + using var connection = new SqliteConnection(ConnectionString); + connection.Open(); + + var sql = + """ + DELETE FROM coding_sessions + WHERE Id = @Id; + """; + + connection.Execute(sql, new { Id = id }); + } + + public void UpdateItem(int id, DateTime start, DateTime end) + { + using var connection = new SqliteConnection(ConnectionString); + connection.Open(); + + var sql = + """ + UPDATE coding_sessions SET StartTime = @StartTime, EndTime = @EndTime + WHERE Id = @Id + """; + var session = new CodingSession { Id = id, StartTime = start, EndTime = end }; + + connection.Execute(sql, session); + } + + public List ViewItems() + { + using var connection = new SqliteConnection(ConnectionString); + connection.Open(); + + var sql = "SELECT * FROM coding_sessions"; + var sessions = connection.Query(sql).ToList(); + + return sessions; + } +} diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Data/Database.cs b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Data/Database.cs index 8ec2306d..e3c78c17 100644 --- a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Data/Database.cs +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Data/Database.cs @@ -1,26 +1,24 @@ using Dapper; using Microsoft.Data.Sqlite; -namespace CodingTracker.DzemalKurtic.Data +namespace CodingTracker.DzemalKurtic.Data; + +internal static class Database { - internal static class Database + public static void Initialize(string connectionString) { - public static void Initialize(string connectionString) - { - using var connection = new SqliteConnection(connectionString); - - connection.Open(); + using var connection = new SqliteConnection(connectionString); + connection.Open(); - var sql = - """ - CREATE TABLE IF NOT EXISTS coding_sessions ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - StartTime TEXT NOT NULL, - EndTime TEXT NOT NULL - ); - """; + var sql = + """ + CREATE TABLE IF NOT EXISTS coding_sessions ( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + StartTime TEXT NOT NULL, + EndTime TEXT NOT NULL + ); + """; - connection.Execute(sql ); - } + connection.Execute(sql ); } } diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/MenuAction.cs b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/MenuAction.cs new file mode 100644 index 00000000..7f67b7db --- /dev/null +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/MenuAction.cs @@ -0,0 +1,12 @@ +using System.ComponentModel; + +namespace CodingTracker.DzemalKurtic; + +internal enum MenuAction +{ + [Description("View Session")] + ViewSessions, + AddSession, + UpdateSession, + DeleteSession +} diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Models/CodingSession.cs b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Models/CodingSession.cs index 074e62cc..3cae86c7 100644 --- a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Models/CodingSession.cs +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Models/CodingSession.cs @@ -2,20 +2,31 @@ using System.Collections.Generic; using System.Text; -namespace CodingTracker.DzemalKurtic.Models +namespace CodingTracker.DzemalKurtic.Models; + +public class CodingSession { - internal class CodingSession + public int Id { get; set; } + public DateTime StartTime { get; set; } + public DateTime EndTime { get; set; } + + public TimeSpan Duration => EndTime - StartTime; + + public CodingSession(int id, DateTime startTime, DateTime endTime) + { + Id = id; + StartTime = startTime; + EndTime = endTime; + } + + public CodingSession(DateTime startTime, DateTime endTime) + { + StartTime = startTime; + EndTime = endTime; + } + + public CodingSession() { - public int Id { get; set; } - public DateTime StartTime { get; set; } - public DateTime EndTime { get; set; } - private TimeSpan Duration => EndTime - StartTime; - public CodingSession(int id, DateTime startTime, DateTime endTime) - { - Id = id; - StartTime = startTime; - EndTime = endTime; - } } } diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Program.cs b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Program.cs index fbe57d69..1003bcf3 100644 --- a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Program.cs +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Program.cs @@ -1,4 +1,6 @@ -using CodingTracker.DzemalKurtic.Data; +using CodingTracker.DzemalKurtic.Controllers; +using CodingTracker.DzemalKurtic.Data; +using CodingTracker.DzemalKurtic.Views; using Microsoft.Extensions.Configuration; IConfiguration config = new ConfigurationBuilder() @@ -7,4 +9,7 @@ var connectionString = config.GetConnectionString("DefaultConnection"); -Database.Initialize(connectionString); \ No newline at end of file +Database.Initialize(connectionString); +var controller = new CodingSessionController(connectionString); +var ui = new UserInterface(controller); +ui.MainMenu(); \ No newline at end of file diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Properties/launchSettings.json b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Properties/launchSettings.json index acc35f31..f7bc628d 100644 --- a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Properties/launchSettings.json +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Properties/launchSettings.json @@ -1,8 +1,7 @@ { "profiles": { "CodingTracker.DzemalKurtic": { - "commandName": "Project", - "workingDirectory": "C:\\Users\\User\\Desktop\\CSharpAcademy\\CodeReviews.Console.CodingTracker\\CodingTracker.DzemalKurtic\\CodingTracker.DzemalKurtic\\" + "commandName": "Project" } } } \ No newline at end of file diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Views/UserInterface.cs b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Views/UserInterface.cs new file mode 100644 index 00000000..97be974a --- /dev/null +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Views/UserInterface.cs @@ -0,0 +1,120 @@ +using CodingTracker.DzemalKurtic.Controllers; +using CodingTracker.DzemalKurtic.Models; +using Microsoft.Data.Sqlite; +using Spectre.Console; +using System.Globalization; + +namespace CodingTracker.DzemalKurtic.Views; + +internal class UserInterface +{ + private readonly CodingSessionController _codingSessionController; + + internal UserInterface(CodingSessionController controller) + { + _codingSessionController = controller; + } + + internal void MainMenu() + { + bool appRunning = true; + while (appRunning) + { + Console.Clear(); + + var choice = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("What do you want to do next?") + .AddChoices(Enum.GetValues()) + .UseConverter(action => action switch + { + MenuAction.ViewSessions => "View all Sessions", + MenuAction.AddSession => "Add a Session", + MenuAction.UpdateSession => "Update a Session", + MenuAction.DeleteSession => "Delete a Session", + })); + + switch (choice) + { + case MenuAction.ViewSessions: + ShowItems(); + break; + case MenuAction.AddSession: + AddItem(); + break; + case MenuAction.UpdateSession: + UpdateItem(); + break; + case MenuAction.DeleteSession: + DeleteItem(); + break; + } + } + } + + internal void ShowItems() + { + var table = new Table(); + table.Border(TableBorder.Rounded); + + table.AddColumn("[yellow]ID[/]"); + table.AddColumn("[yellow]Start Time[/]"); + table.AddColumn("[yellow]End Time[/]"); + table.AddColumn("[yellow]Duration[/]"); + + var sessions = _codingSessionController.ViewItems(); + + foreach (var session in sessions) + { + table.AddRow( + session.Id.ToString(), + $"[cyan]{session.StartTime:dd-MM-yyyy HH:mm}[/]", + $"[yellow]{session.EndTime:dd-MM-yyyy HH:mm}[/]", + $"[green]{session.Duration.TotalHours:F2} hours[/]" + ); + } + + AnsiConsole.Write(table); + AnsiConsole.MarkupLine("Press Any Key to Continue."); + Console.ReadKey(); + } + + internal void AddItem() + { + var start = AnsiConsole.Ask("Enter the start date of the Coding Session: (Format: dd-mm-yy HH:mm)"); + var end = AnsiConsole.Ask("Enter the end date of the Coding Session: (format: dd-mm-yy HH:mm"); + + var startDate = DateTime.ParseExact(start, "dd-MM-yy HH:mm", CultureInfo.InvariantCulture); + var endDate = DateTime.ParseExact(end, "dd-MM-yy HH:mm", CultureInfo.InvariantCulture); + + _codingSessionController.AddItem(startDate, endDate); + AnsiConsole.MarkupLine("Press Any Key to Continue."); + Console.ReadKey(); + } + + internal void UpdateItem() + { + ShowItems(); + + var itemId = AnsiConsole.Ask("Please typt the Id od the item you want to update"); + var start = AnsiConsole.Ask("Enter the start date of the Coding Session: (Format: dd-mm-yy HH:mm)"); + var end = AnsiConsole.Ask("Enter the end date of the Coding Session: (format: dd-mm-yy HH:mm"); + + var startDate = DateTime.ParseExact(start, "dd-MM-yy HH:mm", CultureInfo.InvariantCulture); + var endDate = DateTime.ParseExact(end, "dd-MM-yy HH:mm", CultureInfo.InvariantCulture); + + _codingSessionController.UpdateItem(itemId, startDate, endDate); + AnsiConsole.MarkupLine("Press Any Key to Continue."); + Console.ReadKey(); + } + + internal void DeleteItem() + { + ShowItems(); + + var itemId = AnsiConsole.Ask("Please typt the Id od the item you want to delete"); + _codingSessionController.DeleteItem(itemId); + AnsiConsole.MarkupLine("Press Any Key to Continue."); + Console.ReadKey(); + } +} From 43af9e6f68d002f22ddbb95590fc7898ada3f4f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kurti=C4=87=20D=C5=BEemal?= Date: Mon, 7 Sep 2026 20:15:19 +0200 Subject: [PATCH 4/5] Add Validation --- .../Controllers/CodingSessionController.cs | 11 +- .../CodingTracker.DzemalKurtic/MenuAction.cs | 5 +- .../Models/CodingSession.cs | 6 +- .../CodingTracker.DzemalKurtic/Validation.cs | 40 +++++++ .../Views/UserInterface.cs | 102 +++++++++++++----- 5 files changed, 122 insertions(+), 42 deletions(-) create mode 100644 CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Validation.cs diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Controllers/CodingSessionController.cs b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Controllers/CodingSessionController.cs index c3796f81..d9cc117e 100644 --- a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Controllers/CodingSessionController.cs +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Controllers/CodingSessionController.cs @@ -1,5 +1,4 @@ using CodingTracker.DzemalKurtic.Models; -using CodingTracker.DzemalKurtic.Views; using Dapper; using Microsoft.Data.Sqlite; @@ -30,7 +29,7 @@ INSERT INTO coding_sessions connection.Execute(sql, session); } - public void DeleteItem(int id) + public int DeleteItem(int id) { using var connection = new SqliteConnection(ConnectionString); connection.Open(); @@ -41,10 +40,11 @@ DELETE FROM coding_sessions WHERE Id = @Id; """; - connection.Execute(sql, new { Id = id }); + var rowCount = connection.Execute(sql, new { Id = id }); + return rowCount; } - public void UpdateItem(int id, DateTime start, DateTime end) + public int UpdateItem(int id, DateTime start, DateTime end) { using var connection = new SqliteConnection(ConnectionString); connection.Open(); @@ -56,7 +56,8 @@ public void UpdateItem(int id, DateTime start, DateTime end) """; var session = new CodingSession { Id = id, StartTime = start, EndTime = end }; - connection.Execute(sql, session); + var rowCount = connection.Execute(sql, session); + return rowCount; } public List ViewItems() diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/MenuAction.cs b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/MenuAction.cs index 7f67b7db..a72d6f87 100644 --- a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/MenuAction.cs +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/MenuAction.cs @@ -1,10 +1,7 @@ -using System.ComponentModel; - -namespace CodingTracker.DzemalKurtic; +namespace CodingTracker.DzemalKurtic; internal enum MenuAction { - [Description("View Session")] ViewSessions, AddSession, UpdateSession, diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Models/CodingSession.cs b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Models/CodingSession.cs index 3cae86c7..7d74c070 100644 --- a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Models/CodingSession.cs +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Models/CodingSession.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace CodingTracker.DzemalKurtic.Models; +namespace CodingTracker.DzemalKurtic.Models; public class CodingSession { diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Validation.cs b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Validation.cs new file mode 100644 index 00000000..e1068778 --- /dev/null +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Validation.cs @@ -0,0 +1,40 @@ +using Spectre.Console; +using System.Globalization; + +namespace CodingTracker.DzemalKurtic; + +internal static class Validation +{ + internal static DateTime ValidateDate(string dateInput, string dateName) + { + string date = dateInput; + + while (!DateTime.TryParseExact(date, "dd-MM-yy HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out _)) + { + AnsiConsole.WriteLine("Invalid date. (Format: dd-mm-yy hh:mm). Try again:"); + date = AnsiConsole.Ask($"Enter the {dateName} date of the Coding Session: (Format: dd-mm-yy hh:mm)"); + } + + return DateTime.ParseExact(date, "dd-MM-yy HH:mm", CultureInfo.InvariantCulture); + } + + + internal static int ValidateId(int idInput) + { + int id = idInput; + while (Convert.ToInt32(id) < 0) + { + AnsiConsole.WriteLine("Number can't be negative. Try again."); + id = AnsiConsole.Ask("Please typt the Id od the item you want to update"); + } + + return Convert.ToInt32(id); + } + + internal static bool ValidateTimespan(DateTime start, DateTime end) + { + bool isBigger = end <= start; + if (isBigger) AnsiConsole.WriteLine("End date can not be before start date"); + return isBigger; + } +} diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Views/UserInterface.cs b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Views/UserInterface.cs index 97be974a..cf249e67 100644 --- a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Views/UserInterface.cs +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Views/UserInterface.cs @@ -1,8 +1,5 @@ using CodingTracker.DzemalKurtic.Controllers; -using CodingTracker.DzemalKurtic.Models; -using Microsoft.Data.Sqlite; using Spectre.Console; -using System.Globalization; namespace CodingTracker.DzemalKurtic.Views; @@ -24,7 +21,7 @@ internal void MainMenu() var choice = AnsiConsole.Prompt( new SelectionPrompt() - .Title("What do you want to do next?") + .Title("What do you want to do?") .AddChoices(Enum.GetValues()) .UseConverter(action => action switch { @@ -57,10 +54,10 @@ internal void ShowItems() var table = new Table(); table.Border(TableBorder.Rounded); - table.AddColumn("[yellow]ID[/]"); - table.AddColumn("[yellow]Start Time[/]"); - table.AddColumn("[yellow]End Time[/]"); - table.AddColumn("[yellow]Duration[/]"); + table.AddColumn("ID"); + table.AddColumn("Start Time"); + table.AddColumn("End Time"); + table.AddColumn("Duration"); var sessions = _codingSessionController.ViewItems(); @@ -70,7 +67,7 @@ internal void ShowItems() session.Id.ToString(), $"[cyan]{session.StartTime:dd-MM-yyyy HH:mm}[/]", $"[yellow]{session.EndTime:dd-MM-yyyy HH:mm}[/]", - $"[green]{session.Duration.TotalHours:F2} hours[/]" + $"[green]{session.Duration.Hours} hours {session.Duration.Minutes} minutes[/]" ); } @@ -81,40 +78,89 @@ internal void ShowItems() internal void AddItem() { - var start = AnsiConsole.Ask("Enter the start date of the Coding Session: (Format: dd-mm-yy HH:mm)"); - var end = AnsiConsole.Ask("Enter the end date of the Coding Session: (format: dd-mm-yy HH:mm"); + var start = getDate("start"); + var startDate = Validation.ValidateDate(start, "start"); - var startDate = DateTime.ParseExact(start, "dd-MM-yy HH:mm", CultureInfo.InvariantCulture); - var endDate = DateTime.ParseExact(end, "dd-MM-yy HH:mm", CultureInfo.InvariantCulture); + var end = getDate("end"); + var endDate = Validation.ValidateDate(end, "end"); - _codingSessionController.AddItem(startDate, endDate); - AnsiConsole.MarkupLine("Press Any Key to Continue."); - Console.ReadKey(); + var isBigger = Validation.ValidateTimespan(startDate, endDate); + + if (!isBigger) + { + _codingSessionController.AddItem(startDate, endDate); + AnsiConsole.MarkupLine("Press Any Key to Continue."); + Console.ReadKey(); + } + else + { + AnsiConsole.MarkupLine("Press Any Key to Continue."); + Console.ReadKey(); + + Console.Clear(); + AddItem(); + } + } internal void UpdateItem() { ShowItems(); - var itemId = AnsiConsole.Ask("Please typt the Id od the item you want to update"); - var start = AnsiConsole.Ask("Enter the start date of the Coding Session: (Format: dd-mm-yy HH:mm)"); - var end = AnsiConsole.Ask("Enter the end date of the Coding Session: (format: dd-mm-yy HH:mm"); + var itemId = getId("update"); + var id = Validation.ValidateId(itemId); - var startDate = DateTime.ParseExact(start, "dd-MM-yy HH:mm", CultureInfo.InvariantCulture); - var endDate = DateTime.ParseExact(end, "dd-MM-yy HH:mm", CultureInfo.InvariantCulture); + var start = getDate("start"); + var startDate = Validation.ValidateDate(start, "start"); - _codingSessionController.UpdateItem(itemId, startDate, endDate); - AnsiConsole.MarkupLine("Press Any Key to Continue."); - Console.ReadKey(); + var end = getDate("end"); + var endDate = Validation.ValidateDate(end, "end"); + + var rowCount = _codingSessionController.UpdateItem(itemId, startDate, endDate); + if (rowCount == 0) + { + AnsiConsole.MarkupLine($"Session with id {id} doesn't exist."); + AnsiConsole.MarkupLine("Press Any Key to Continue."); + Console.ReadKey(); + Console.Clear(); + MainMenu(); + }else + { + AnsiConsole.MarkupLine("Press Any Key to Continue."); + Console.ReadKey(); + } } internal void DeleteItem() { ShowItems(); - var itemId = AnsiConsole.Ask("Please typt the Id od the item you want to delete"); - _codingSessionController.DeleteItem(itemId); - AnsiConsole.MarkupLine("Press Any Key to Continue."); - Console.ReadKey(); + var itemId = getId("delete"); + var id = Validation.ValidateId(itemId); + + var rowCount = _codingSessionController.DeleteItem(id); + if (rowCount == 0) + { + AnsiConsole.MarkupLine($"Session with id {id} doesn't exist.\n"); + AnsiConsole.MarkupLine("Press Any Key to Continue.\n"); + Console.ReadKey(); + Console.Clear(); + DeleteItem(); + } + else + { + AnsiConsole.MarkupLine("Press Any Key to Continue."); + Console.ReadKey(); + } + } + + private string getDate(string time) + { + return AnsiConsole.Ask($"Enter the {time} time of the Coding Session: (Format: dd-mm-yy HH:mm)"); + } + + private int getId(string action) + { + return AnsiConsole.Ask($"Please typt the Id od the item you want to {action}"); } } From 1652bd4bd4a93ea9774c9350b3cc82106f201501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kurti=C4=87=20D=C5=BEemal?= Date: Mon, 7 Sep 2026 20:50:48 +0200 Subject: [PATCH 5/5] Add Readme --- .../Data/Database.cs | 2 +- .../CodingTracker.DzemalKurtic/README.md | 69 +++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/README.md diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Data/Database.cs b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Data/Database.cs index e3c78c17..146c9a43 100644 --- a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Data/Database.cs +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/Data/Database.cs @@ -19,6 +19,6 @@ EndTime TEXT NOT NULL ); """; - connection.Execute(sql ); + connection.Execute(sql); } } diff --git a/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/README.md b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/README.md new file mode 100644 index 00000000..9589cc56 --- /dev/null +++ b/CodingTracker.DzemalKurtic/CodingTracker.DzemalKurtic/README.md @@ -0,0 +1,69 @@ +**Project requirements:** + + + +* Logging occurrence of a coding session +* Users need to be bale to input the start date of the coding session +* Users need to be bale to input the end date of the coding session +* Duration of the sessions should be calculated from these values +* App should use a real database +* Users should be able to insert, delete, update and view their coding sessions +* All input errors should be handled +* Only Dapper should be used + + + +**How the App works?** + + + +When a user starts the app, he sees a menu with options: + + + +\- View all Sessions + +\- Add a Session + +\- Update a Session + +\- Delete a Session + + + + + +Choosing "View all Sessions" will show him a table with all the sessions that are recorded in the database. + + + +Choosing "Add a Session" will open another screen where he will be able to enter a start time in dd-mm-yy hh:mm format. + +After entering it he will be able to enter a end time in dd-mm-yy hh:mm format for the session. + + + +Choosing "Update a Session" will bring up a new screen where a user will be able to update an existing record. + +You must use Id of the record that exists in the database. + +After choosing an Id user will be able to enter new start time and time for the session. + + + +Choosing "Delete a Session" bring up a new screen where a use will be able to delete the sessions that are already recorded. + +You must use Id of the record that exists in the database. + + + + + +**Thoughts on the project:** + + + +This project made me use appsettings.json where I put the connection string to the Sqlite database. Dapper is easier to use than ADO.NET. For user interface I used spectre console, which has quite a few neat features. I really like the table feature. Also it's easier to get a list of options and a correct answer. + +Hardest part was figuring out how to convert between a model and a database because Sqlite doesn't have a date data type. I was using strings in the model because I couldn't get it to work with DateTime type. But then I was wondering how to calculate the duration of the session. +