From 44864437a24474267b3627063199530fcad281d3 Mon Sep 17 00:00:00 2001 From: "c0d3.m0nk3y" Date: Tue, 12 May 2026 12:05:50 -0400 Subject: [PATCH] Continued development --- Data/TaskData.cs | 83 ++++++++++++++++----------- Forms/MainForm.Designer.cs | 83 +++++++++++++++++++++++++++ Forms/MainForm.cs | 111 ++++++++++++++++++++++--------------- Forms/TaskForm.Designer.cs | 2 - Models/PTask.cs | 16 ++++++ 5 files changed, 218 insertions(+), 77 deletions(-) diff --git a/Data/TaskData.cs b/Data/TaskData.cs index 0317c08..e625a16 100644 --- a/Data/TaskData.cs +++ b/Data/TaskData.cs @@ -36,22 +36,27 @@ namespace trakker.Data WITH RECURSIVE TaskHierarchy AS ( -- Anchor: starting task(s) SELECT - task_id, - project_id, - title, - description, - status, - priority, - due_date, - estimated_hours, - actual_hours, - hourly_rate, - parent_task_id, - created_at, - updated_at, + t0.task_id, + t0.project_id, + t0.title, + t0.description, + t0.status, + x0.display AS status_name, + t0.priority, + x1.display AS priority_name, + t0.due_date, + t0.estimated_hours, + t0.actual_hours, + t0.hourly_rate, + (t0.actual_hours * t0.hourly_rate) AS amount, + t0.parent_task_id, + t0.created_at, + t0.updated_at, 0 AS level, - title AS path - FROM tasks + t0.title AS path + FROM tasks t0 + JOIN (SELECT value, display FROm lov WHERE source = 'task.status') x0 ON t0.status = x0.value + JOIN (SELECT value, display FROm lov WHERE source = 'task.priority') x1 ON t0.priority = x1.value WHERE {whereClause} UNION ALL @@ -63,17 +68,22 @@ namespace trakker.Data t.title, t.description, t.status, + x0.display AS status_name, t.priority, + x1.display AS priority_name, t.due_date, t.estimated_hours, t.actual_hours, t.hourly_rate, + (t.actual_hours * t.hourly_rate) AS amount, t.parent_task_id, t.created_at, t.updated_at, th.level + 1, th.path || ' > ' || t.title FROM tasks t + JOIN (SELECT value, display FROm lov WHERE source = 'task.status') x0 ON t.status = x0.value + JOIN (SELECT value, display FROm lov WHERE source = 'task.priority') x1 ON t.priority = x1.value JOIN TaskHierarchy th ON t.parent_task_id = th.task_id ) SELECT * @@ -97,14 +107,17 @@ namespace trakker.Data var _var3 = reader.GetOrdinal("title"); var _var4 = reader.GetOrdinal("description"); var _var5 = reader.GetOrdinal("status"); - var _var6 = reader.GetOrdinal("priority"); - var _var7 = reader.GetOrdinal("due_date"); - var _var8 = reader.GetOrdinal("estimated_hours"); - var _var9 = reader.GetOrdinal("actual_hours"); - var _var10 = reader.GetOrdinal("hourly_rate"); - var _var11 = reader.GetOrdinal("parent_task_id"); - var _var12 = reader.GetOrdinal("created_at"); - var _var13 = reader.GetOrdinal("updated_at"); + var _var6 = reader.GetOrdinal("status_name"); + var _var7 = reader.GetOrdinal("priority"); + var _var8 = reader.GetOrdinal("priority_name"); + var _var9 = reader.GetOrdinal("due_date"); + var _var10 = reader.GetOrdinal("estimated_hours"); + var _var11 = reader.GetOrdinal("actual_hours"); + var _var12 = reader.GetOrdinal("hourly_rate"); + var _var13 = reader.GetOrdinal("amount"); + var _var14 = reader.GetOrdinal("parent_task_id"); + var _var15 = reader.GetOrdinal("created_at"); + var _var16 = reader.GetOrdinal("updated_at"); while (reader.Read()) { @@ -115,14 +128,17 @@ namespace trakker.Data Title = reader.GetString(_var3), Description = reader.GetString(_var4), Status = reader.GetString(_var5), - Priority = reader.GetString(_var6), - DueDate = reader.IsDBNull(_var7) ? null : reader.GetDateTime(_var7), - EstimatedHours = reader.IsDBNull(_var8) ? null : reader.GetDouble(_var8), - ActualHours = reader.IsDBNull(_var9) ? null : reader.GetDouble(_var9), - HourlyRate = reader.IsDBNull(_var10) ? null : reader.GetDouble(_var10), - ParentTaskId = reader.IsDBNull(_var11) ? null : reader.GetString(_var11), - CreatedAt = reader.IsDBNull(_var12) ? null : reader.GetDateTime(_var12), - UpdatedAt = reader.IsDBNull(_var13) ? null : reader.GetDateTime(_var13), + StatusName = reader.GetString(_var6), + Priority = reader.GetString(_var7), + PriorityName = reader.GetString(_var8), + DueDate = reader.IsDBNull(_var9) ? null : reader.GetDateTime(_var9), + EstimatedHours = reader.IsDBNull(_var10) ? null : reader.GetDouble(_var10), + ActualHours = reader.IsDBNull(_var11) ? null : reader.GetDouble(_var11), + HourlyRate = reader.IsDBNull(_var12) ? null : reader.GetDouble(_var12), + Amount = reader.IsDBNull(_var13) ? null : reader.GetDouble(_var13), + ParentTaskId = reader.IsDBNull(_var14) ? null : reader.GetString(_var14), + CreatedAt = reader.IsDBNull(_var15) ? null : reader.GetDateTime(_var15), + UpdatedAt = reader.IsDBNull(_var16) ? null : reader.GetDateTime(_var16), }); } @@ -477,6 +493,11 @@ namespace trakker.Data using var cmd = conn.CreateCommand(); cmd.CommandText = sql; + if (taskId != null) + { + cmd.Parameters.AddWithValue("$task_id", taskId); + } + using var reader = cmd.ExecuteReader(); var _var1 = reader.GetOrdinal("task_comment_id"); diff --git a/Forms/MainForm.Designer.cs b/Forms/MainForm.Designer.cs index 4cbfbff..e5afef4 100644 --- a/Forms/MainForm.Designer.cs +++ b/Forms/MainForm.Designer.cs @@ -53,6 +53,11 @@ addACommentToolStripMenuItem = new ToolStripMenuItem(); splitContainerTasks2 = new SplitContainer(); dataGridViewProjectTasks = new DataGridView(); + tableLayoutPanelTasks2 = new TableLayoutPanel(); + groupBoxTaskDescription = new GroupBox(); + richTextBoxTaskDescription = new RichTextBox(); + groupBoxTaskComments = new GroupBox(); + richTextBoxTaskComments = new RichTextBox(); MainForm_MenuStrip.SuspendLayout(); tabControlMainForm.SuspendLayout(); MainForm_TabPage2.SuspendLayout(); @@ -70,8 +75,12 @@ contextMenuStripTreeviewTasks.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)splitContainerTasks2).BeginInit(); splitContainerTasks2.Panel1.SuspendLayout(); + splitContainerTasks2.Panel2.SuspendLayout(); splitContainerTasks2.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)dataGridViewProjectTasks).BeginInit(); + tableLayoutPanelTasks2.SuspendLayout(); + groupBoxTaskDescription.SuspendLayout(); + groupBoxTaskComments.SuspendLayout(); SuspendLayout(); // // MainForm_MenuStrip @@ -308,6 +317,10 @@ // splitContainerTasks2.Panel1 // splitContainerTasks2.Panel1.Controls.Add(dataGridViewProjectTasks); + // + // splitContainerTasks2.Panel2 + // + splitContainerTasks2.Panel2.Controls.Add(tableLayoutPanelTasks2); splitContainerTasks2.Size = new Size(1234, 960); splitContainerTasks2.SplitterDistance = 411; splitContainerTasks2.TabIndex = 0; @@ -325,6 +338,67 @@ dataGridViewProjectTasks.Size = new Size(1234, 411); dataGridViewProjectTasks.TabIndex = 0; // + // tableLayoutPanelTasks2 + // + tableLayoutPanelTasks2.ColumnCount = 1; + tableLayoutPanelTasks2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F)); + tableLayoutPanelTasks2.Controls.Add(groupBoxTaskDescription, 0, 0); + tableLayoutPanelTasks2.Controls.Add(groupBoxTaskComments, 0, 1); + tableLayoutPanelTasks2.Dock = DockStyle.Fill; + tableLayoutPanelTasks2.Location = new Point(0, 0); + tableLayoutPanelTasks2.Name = "tableLayoutPanelTasks2"; + tableLayoutPanelTasks2.RowCount = 2; + tableLayoutPanelTasks2.RowStyles.Add(new RowStyle(SizeType.Percent, 25F)); + tableLayoutPanelTasks2.RowStyles.Add(new RowStyle(SizeType.Percent, 75F)); + tableLayoutPanelTasks2.Size = new Size(1234, 545); + tableLayoutPanelTasks2.TabIndex = 0; + // + // groupBoxTaskDescription + // + groupBoxTaskDescription.Controls.Add(richTextBoxTaskDescription); + groupBoxTaskDescription.Dock = DockStyle.Fill; + groupBoxTaskDescription.Location = new Point(3, 3); + groupBoxTaskDescription.Name = "groupBoxTaskDescription"; + groupBoxTaskDescription.Size = new Size(1228, 130); + groupBoxTaskDescription.TabIndex = 0; + groupBoxTaskDescription.TabStop = false; + groupBoxTaskDescription.Text = "Description"; + // + // richTextBoxTaskDescription + // + richTextBoxTaskDescription.BackColor = SystemColors.Control; + richTextBoxTaskDescription.BorderStyle = BorderStyle.None; + richTextBoxTaskDescription.Dock = DockStyle.Fill; + richTextBoxTaskDescription.Location = new Point(3, 35); + richTextBoxTaskDescription.Name = "richTextBoxTaskDescription"; + richTextBoxTaskDescription.ReadOnly = true; + richTextBoxTaskDescription.Size = new Size(1222, 92); + richTextBoxTaskDescription.TabIndex = 0; + richTextBoxTaskDescription.Text = ""; + // + // groupBoxTaskComments + // + groupBoxTaskComments.Controls.Add(richTextBoxTaskComments); + groupBoxTaskComments.Dock = DockStyle.Fill; + groupBoxTaskComments.Location = new Point(3, 139); + groupBoxTaskComments.Name = "groupBoxTaskComments"; + groupBoxTaskComments.Size = new Size(1228, 403); + groupBoxTaskComments.TabIndex = 1; + groupBoxTaskComments.TabStop = false; + groupBoxTaskComments.Text = "Comments"; + // + // richTextBoxTaskComments + // + richTextBoxTaskComments.BackColor = SystemColors.Control; + richTextBoxTaskComments.BorderStyle = BorderStyle.None; + richTextBoxTaskComments.Dock = DockStyle.Fill; + richTextBoxTaskComments.Location = new Point(3, 35); + richTextBoxTaskComments.Name = "richTextBoxTaskComments"; + richTextBoxTaskComments.ReadOnly = true; + richTextBoxTaskComments.Size = new Size(1222, 365); + richTextBoxTaskComments.TabIndex = 1; + richTextBoxTaskComments.Text = ""; + // // MainForm // AutoScaleDimensions = new SizeF(13F, 32F); @@ -353,9 +427,13 @@ splitContainerTasks1.ResumeLayout(false); contextMenuStripTreeviewTasks.ResumeLayout(false); splitContainerTasks2.Panel1.ResumeLayout(false); + splitContainerTasks2.Panel2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)splitContainerTasks2).EndInit(); splitContainerTasks2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)dataGridViewProjectTasks).EndInit(); + tableLayoutPanelTasks2.ResumeLayout(false); + groupBoxTaskDescription.ResumeLayout(false); + groupBoxTaskComments.ResumeLayout(false); ResumeLayout(false); PerformLayout(); } @@ -386,5 +464,10 @@ private ToolStripMenuItem addACommentToolStripMenuItem; private SplitContainer splitContainerTasks2; private DataGridView dataGridViewProjectTasks; + private TableLayoutPanel tableLayoutPanelTasks2; + private GroupBox groupBoxTaskDescription; + private GroupBox groupBoxTaskComments; + private RichTextBox richTextBoxTaskDescription; + private RichTextBox richTextBoxTaskComments; } } diff --git a/Forms/MainForm.cs b/Forms/MainForm.cs index 2818611..dd217e2 100644 --- a/Forms/MainForm.cs +++ b/Forms/MainForm.cs @@ -1,4 +1,6 @@ using Microsoft.Data.Sqlite; +using newcle.us.Forms; +using newcle.us.Utilities; using System.ComponentModel; using trakker.Data; using trakker.Forms; @@ -476,7 +478,36 @@ namespace trakker private void addACommentToolStripMenuItem_Click(object sender, EventArgs e) { - + if (treeViewTasks1.SelectedNode == null) return; + PTaskFS? selectedTask = treeViewTasks1.SelectedNode.Tag as PTaskFS; + //MessageBox.Show(selectedTask != null ? selectedTask.ToString() : "No task selected", "Edit Task", MessageBoxButtons.OK, MessageBoxIcon.Information); + if (selectedTask?.Parent == "/" || selectedTask?.GUID == "/") + { + MessageBox.Show("Cannot comment on root node", "Add Comment", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + TextAreaForm textAreaForm = new TextAreaForm("Add / Edit Comment"); + DialogResult result = textAreaForm.ShowDialog(this); + if (result == DialogResult.OK) + { + if (string.IsNullOrEmpty(textAreaForm.Content)) return; + TaskData taskData = new TaskData(connectionString); + try + { + PTaskComment comment = new() + { + TaskId = selectedTask?.GUID ?? string.Empty, + Comment = textAreaForm.Content ?? string.Empty, + }; + taskData.SaveComment(comment); + DialogExtensions.GenericSuccess($"Comment saved successfully for task '{selectedTask!.Node}'."); + dataGridViewProjectTasks_SelectionChanged(null, null); + } + catch (Exception ex) + { + MessageBox.Show($"Error deleting task: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } } public void InitDataGridViewProjectTasks() { @@ -511,7 +542,7 @@ namespace trakker var textColumn = new DataGridViewTextBoxColumn { AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill, - DataPropertyName = "Status", + DataPropertyName = "StatusName", Name = "Status", Visible = true, }; @@ -523,7 +554,7 @@ namespace trakker var textColumn = new DataGridViewTextBoxColumn { AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill, - DataPropertyName = "Priority", + DataPropertyName = "PriorityName", Name = "Priority", Visible = true, }; @@ -586,52 +617,44 @@ namespace trakker dataGridViewProjectTasks.Columns.Add(textColumn); } - dataGridViewProjectTasks.Click += (s, e) => + // 7. Amount - Financial info at the end { - if (dataGridViewProjectTasks.SelectedRows.Count > 0) + var textColumn = new DataGridViewTextBoxColumn { - var selectedIdx = dataGridViewProjectTasks.SelectedRows[0].Index; - var selectedProject = dataGridViewProjectTasks.SelectedRows[0].DataBoundItem as Project; - if (selectedProject != null) + AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill, + Name = "Amount", + DataPropertyName = "Amount", + Visible = true, + }; + textColumn.DefaultCellStyle.Format = "$#,##0.00"; + textColumn.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; + textColumn.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleRight; + dataGridViewProjectTasks.Columns.Add(textColumn); + } + + dataGridViewProjectTasks.SelectionChanged += (s, e) => + { + dataGridViewProjectTasks_SelectionChanged(s, e); + }; + } + public void dataGridViewProjectTasks_SelectionChanged(object? sender, EventArgs? e) + { + if (dataGridViewProjectTasks.SelectedRows.Count > 0) + { + var selectedIdx = dataGridViewProjectTasks.SelectedRows[0].Index; + var selectedTask = dataGridViewProjectTasks.SelectedRows[0].DataBoundItem as PTask; + if (selectedTask != null) + { + richTextBoxTaskDescription.Text = selectedTask.Description; + TaskData taskData = new TaskData(connectionString); + List comments = taskData.GetComments(selectedTask.TaskId ?? string.Empty); + richTextBoxTaskComments.Clear(); + foreach (var comment in comments) { - //var dialog = new ProjectForm(selectedProject, _ctrl.GetClients(), _ctrl.GetLOV("project.status")); - //if (dialog.ShowDialog(this) == DialogResult.OK) - //{ - // Project project = dialog.Project; - // ProjectData projectData = new ProjectData(connectionString); - // try - // { - // projectData.Upsert(project); - // _ctrl.LoadProjects(); // Reload projects to update the DataGridView with any changes - // dataGridViewProjectTasks.ClearSelection(); - // if (selectedIdx >= 0 && selectedIdx < dataGridViewProjectTasks.Rows.Count) - // { - // dataGridViewProjectTasks.Rows[selectedIdx].Selected = true; - // } - // } - // catch (Exception ex) - // { - // MessageBox.Show($"Error saving project: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - // } - //} - //return; + richTextBoxTaskComments.AppendText($"{comment.CreatedAt}\n---\n{comment.Comment}\n\n"); } } - }; - - //dataGridViewClients.SelectionChanged += (s, e) => - //{ - // if (dataGridViewClients.SelectedRows.Count > 0) - // { - // var selectedClient = dataGridViewClients.SelectedRows[0].DataBoundItem as Client; - // if (selectedClient != null) - // { - // // Handle the selected client as needed - // // MessageBox.Show($"Selected Client: {selectedClient.AddressStreet}", "Client Selected", MessageBoxButtons.OK, MessageBoxIcon.Information ); - // } - // } - //}; - + } } public void FillDataGridViewProjectTasks(BindingSource tasks) { diff --git a/Forms/TaskForm.Designer.cs b/Forms/TaskForm.Designer.cs index bdd687f..9b0a47a 100644 --- a/Forms/TaskForm.Designer.cs +++ b/Forms/TaskForm.Designer.cs @@ -383,12 +383,10 @@ private GroupBox groupBoxNewTask; private TableLayoutPanel tableLayoutPanel1; private TableLayoutPanel tableLayoutPanel2; - private Label labelClient; private Label labelTitle; private Label labelDescription; private Label labelHoursEst; private TextBox textBoxTitle; - private ComboBox comboBoxClient; private TableLayoutPanel tableLayoutPanel6; private Label labelHoursActual; private RichTextBox richTextBoxDescription; diff --git a/Models/PTask.cs b/Models/PTask.cs index a25c030..3a9013e 100644 --- a/Models/PTask.cs +++ b/Models/PTask.cs @@ -25,9 +25,12 @@ public string? Description { get; set; } public string Status { get; set; } = "todo"; + public string StatusName { get; set; } = string.Empty; public string Priority { get; set; } = "medium"; + public string PriorityName { get; set; } = string.Empty; + public DateTime? DueDate { get; set; } public double? EstimatedHours { get; set; } = 0; @@ -36,6 +39,8 @@ public double? HourlyRate { get; set; } = 0; + public double? Amount { get; set; } = 0; + public string? ParentTaskId { get; set; } public DateTime? CreatedAt { get; set; } @@ -50,7 +55,9 @@ Title: {Title} Description: {Description} Status: {Status} + StatusName: {StatusName} Priority: {Priority} + PriorityName: {PriorityName} DueDate: {DueDate} EstimatedHours: {EstimatedHours} ActualHours: {ActualHours} @@ -96,6 +103,15 @@ public string? Comment { get; set; } = string.Empty; public DateTime? CreatedAt { get; set; } + public override string ToString() + { + return @$" + TaskCommentId: {TaskCommentId} + TaskId: {TaskId} + Comment: {Comment} + CreatedAt: {CreatedAt} + "; + } } }