Continued development

This commit is contained in:
c0d3.m0nk3y 2026-05-12 12:05:50 -04:00
parent 74b01247ed
commit 44864437a2
5 changed files with 218 additions and 77 deletions

View File

@ -36,22 +36,27 @@ namespace trakker.Data
WITH RECURSIVE TaskHierarchy AS ( WITH RECURSIVE TaskHierarchy AS (
-- Anchor: starting task(s) -- Anchor: starting task(s)
SELECT SELECT
task_id, t0.task_id,
project_id, t0.project_id,
title, t0.title,
description, t0.description,
status, t0.status,
priority, x0.display AS status_name,
due_date, t0.priority,
estimated_hours, x1.display AS priority_name,
actual_hours, t0.due_date,
hourly_rate, t0.estimated_hours,
parent_task_id, t0.actual_hours,
created_at, t0.hourly_rate,
updated_at, (t0.actual_hours * t0.hourly_rate) AS amount,
t0.parent_task_id,
t0.created_at,
t0.updated_at,
0 AS level, 0 AS level,
title AS path t0.title AS path
FROM tasks 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 WHERE
{whereClause} {whereClause}
UNION ALL UNION ALL
@ -63,17 +68,22 @@ namespace trakker.Data
t.title, t.title,
t.description, t.description,
t.status, t.status,
x0.display AS status_name,
t.priority, t.priority,
x1.display AS priority_name,
t.due_date, t.due_date,
t.estimated_hours, t.estimated_hours,
t.actual_hours, t.actual_hours,
t.hourly_rate, t.hourly_rate,
(t.actual_hours * t.hourly_rate) AS amount,
t.parent_task_id, t.parent_task_id,
t.created_at, t.created_at,
t.updated_at, t.updated_at,
th.level + 1, th.level + 1,
th.path || ' > ' || t.title th.path || ' > ' || t.title
FROM tasks t 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 JOIN TaskHierarchy th ON t.parent_task_id = th.task_id
) )
SELECT * SELECT *
@ -97,14 +107,17 @@ namespace trakker.Data
var _var3 = reader.GetOrdinal("title"); var _var3 = reader.GetOrdinal("title");
var _var4 = reader.GetOrdinal("description"); var _var4 = reader.GetOrdinal("description");
var _var5 = reader.GetOrdinal("status"); var _var5 = reader.GetOrdinal("status");
var _var6 = reader.GetOrdinal("priority"); var _var6 = reader.GetOrdinal("status_name");
var _var7 = reader.GetOrdinal("due_date"); var _var7 = reader.GetOrdinal("priority");
var _var8 = reader.GetOrdinal("estimated_hours"); var _var8 = reader.GetOrdinal("priority_name");
var _var9 = reader.GetOrdinal("actual_hours"); var _var9 = reader.GetOrdinal("due_date");
var _var10 = reader.GetOrdinal("hourly_rate"); var _var10 = reader.GetOrdinal("estimated_hours");
var _var11 = reader.GetOrdinal("parent_task_id"); var _var11 = reader.GetOrdinal("actual_hours");
var _var12 = reader.GetOrdinal("created_at"); var _var12 = reader.GetOrdinal("hourly_rate");
var _var13 = reader.GetOrdinal("updated_at"); 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()) while (reader.Read())
{ {
@ -115,14 +128,17 @@ namespace trakker.Data
Title = reader.GetString(_var3), Title = reader.GetString(_var3),
Description = reader.GetString(_var4), Description = reader.GetString(_var4),
Status = reader.GetString(_var5), Status = reader.GetString(_var5),
Priority = reader.GetString(_var6), StatusName = reader.GetString(_var6),
DueDate = reader.IsDBNull(_var7) ? null : reader.GetDateTime(_var7), Priority = reader.GetString(_var7),
EstimatedHours = reader.IsDBNull(_var8) ? null : reader.GetDouble(_var8), PriorityName = reader.GetString(_var8),
ActualHours = reader.IsDBNull(_var9) ? null : reader.GetDouble(_var9), DueDate = reader.IsDBNull(_var9) ? null : reader.GetDateTime(_var9),
HourlyRate = reader.IsDBNull(_var10) ? null : reader.GetDouble(_var10), EstimatedHours = reader.IsDBNull(_var10) ? null : reader.GetDouble(_var10),
ParentTaskId = reader.IsDBNull(_var11) ? null : reader.GetString(_var11), ActualHours = reader.IsDBNull(_var11) ? null : reader.GetDouble(_var11),
CreatedAt = reader.IsDBNull(_var12) ? null : reader.GetDateTime(_var12), HourlyRate = reader.IsDBNull(_var12) ? null : reader.GetDouble(_var12),
UpdatedAt = reader.IsDBNull(_var13) ? null : reader.GetDateTime(_var13), 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(); using var cmd = conn.CreateCommand();
cmd.CommandText = sql; cmd.CommandText = sql;
if (taskId != null)
{
cmd.Parameters.AddWithValue("$task_id", taskId);
}
using var reader = cmd.ExecuteReader(); using var reader = cmd.ExecuteReader();
var _var1 = reader.GetOrdinal("task_comment_id"); var _var1 = reader.GetOrdinal("task_comment_id");

View File

@ -53,6 +53,11 @@
addACommentToolStripMenuItem = new ToolStripMenuItem(); addACommentToolStripMenuItem = new ToolStripMenuItem();
splitContainerTasks2 = new SplitContainer(); splitContainerTasks2 = new SplitContainer();
dataGridViewProjectTasks = new DataGridView(); dataGridViewProjectTasks = new DataGridView();
tableLayoutPanelTasks2 = new TableLayoutPanel();
groupBoxTaskDescription = new GroupBox();
richTextBoxTaskDescription = new RichTextBox();
groupBoxTaskComments = new GroupBox();
richTextBoxTaskComments = new RichTextBox();
MainForm_MenuStrip.SuspendLayout(); MainForm_MenuStrip.SuspendLayout();
tabControlMainForm.SuspendLayout(); tabControlMainForm.SuspendLayout();
MainForm_TabPage2.SuspendLayout(); MainForm_TabPage2.SuspendLayout();
@ -70,8 +75,12 @@
contextMenuStripTreeviewTasks.SuspendLayout(); contextMenuStripTreeviewTasks.SuspendLayout();
((System.ComponentModel.ISupportInitialize)splitContainerTasks2).BeginInit(); ((System.ComponentModel.ISupportInitialize)splitContainerTasks2).BeginInit();
splitContainerTasks2.Panel1.SuspendLayout(); splitContainerTasks2.Panel1.SuspendLayout();
splitContainerTasks2.Panel2.SuspendLayout();
splitContainerTasks2.SuspendLayout(); splitContainerTasks2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)dataGridViewProjectTasks).BeginInit(); ((System.ComponentModel.ISupportInitialize)dataGridViewProjectTasks).BeginInit();
tableLayoutPanelTasks2.SuspendLayout();
groupBoxTaskDescription.SuspendLayout();
groupBoxTaskComments.SuspendLayout();
SuspendLayout(); SuspendLayout();
// //
// MainForm_MenuStrip // MainForm_MenuStrip
@ -308,6 +317,10 @@
// splitContainerTasks2.Panel1 // splitContainerTasks2.Panel1
// //
splitContainerTasks2.Panel1.Controls.Add(dataGridViewProjectTasks); splitContainerTasks2.Panel1.Controls.Add(dataGridViewProjectTasks);
//
// splitContainerTasks2.Panel2
//
splitContainerTasks2.Panel2.Controls.Add(tableLayoutPanelTasks2);
splitContainerTasks2.Size = new Size(1234, 960); splitContainerTasks2.Size = new Size(1234, 960);
splitContainerTasks2.SplitterDistance = 411; splitContainerTasks2.SplitterDistance = 411;
splitContainerTasks2.TabIndex = 0; splitContainerTasks2.TabIndex = 0;
@ -325,6 +338,67 @@
dataGridViewProjectTasks.Size = new Size(1234, 411); dataGridViewProjectTasks.Size = new Size(1234, 411);
dataGridViewProjectTasks.TabIndex = 0; 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 // MainForm
// //
AutoScaleDimensions = new SizeF(13F, 32F); AutoScaleDimensions = new SizeF(13F, 32F);
@ -353,9 +427,13 @@
splitContainerTasks1.ResumeLayout(false); splitContainerTasks1.ResumeLayout(false);
contextMenuStripTreeviewTasks.ResumeLayout(false); contextMenuStripTreeviewTasks.ResumeLayout(false);
splitContainerTasks2.Panel1.ResumeLayout(false); splitContainerTasks2.Panel1.ResumeLayout(false);
splitContainerTasks2.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)splitContainerTasks2).EndInit(); ((System.ComponentModel.ISupportInitialize)splitContainerTasks2).EndInit();
splitContainerTasks2.ResumeLayout(false); splitContainerTasks2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)dataGridViewProjectTasks).EndInit(); ((System.ComponentModel.ISupportInitialize)dataGridViewProjectTasks).EndInit();
tableLayoutPanelTasks2.ResumeLayout(false);
groupBoxTaskDescription.ResumeLayout(false);
groupBoxTaskComments.ResumeLayout(false);
ResumeLayout(false); ResumeLayout(false);
PerformLayout(); PerformLayout();
} }
@ -386,5 +464,10 @@
private ToolStripMenuItem addACommentToolStripMenuItem; private ToolStripMenuItem addACommentToolStripMenuItem;
private SplitContainer splitContainerTasks2; private SplitContainer splitContainerTasks2;
private DataGridView dataGridViewProjectTasks; private DataGridView dataGridViewProjectTasks;
private TableLayoutPanel tableLayoutPanelTasks2;
private GroupBox groupBoxTaskDescription;
private GroupBox groupBoxTaskComments;
private RichTextBox richTextBoxTaskDescription;
private RichTextBox richTextBoxTaskComments;
} }
} }

View File

@ -1,4 +1,6 @@
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using newcle.us.Forms;
using newcle.us.Utilities;
using System.ComponentModel; using System.ComponentModel;
using trakker.Data; using trakker.Data;
using trakker.Forms; using trakker.Forms;
@ -476,7 +478,36 @@ namespace trakker
private void addACommentToolStripMenuItem_Click(object sender, EventArgs e) 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() public void InitDataGridViewProjectTasks()
{ {
@ -511,7 +542,7 @@ namespace trakker
var textColumn = new DataGridViewTextBoxColumn var textColumn = new DataGridViewTextBoxColumn
{ {
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill, AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
DataPropertyName = "Status", DataPropertyName = "StatusName",
Name = "Status", Name = "Status",
Visible = true, Visible = true,
}; };
@ -523,7 +554,7 @@ namespace trakker
var textColumn = new DataGridViewTextBoxColumn var textColumn = new DataGridViewTextBoxColumn
{ {
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill, AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
DataPropertyName = "Priority", DataPropertyName = "PriorityName",
Name = "Priority", Name = "Priority",
Visible = true, Visible = true,
}; };
@ -586,52 +617,44 @@ namespace trakker
dataGridViewProjectTasks.Columns.Add(textColumn); 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; AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
var selectedProject = dataGridViewProjectTasks.SelectedRows[0].DataBoundItem as Project; Name = "Amount",
if (selectedProject != null) 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<PTaskComment> 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")); richTextBoxTaskComments.AppendText($"{comment.CreatedAt}\n---\n{comment.Comment}\n\n");
//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;
} }
} }
}; }
//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) public void FillDataGridViewProjectTasks(BindingSource tasks)
{ {

View File

@ -383,12 +383,10 @@
private GroupBox groupBoxNewTask; private GroupBox groupBoxNewTask;
private TableLayoutPanel tableLayoutPanel1; private TableLayoutPanel tableLayoutPanel1;
private TableLayoutPanel tableLayoutPanel2; private TableLayoutPanel tableLayoutPanel2;
private Label labelClient;
private Label labelTitle; private Label labelTitle;
private Label labelDescription; private Label labelDescription;
private Label labelHoursEst; private Label labelHoursEst;
private TextBox textBoxTitle; private TextBox textBoxTitle;
private ComboBox comboBoxClient;
private TableLayoutPanel tableLayoutPanel6; private TableLayoutPanel tableLayoutPanel6;
private Label labelHoursActual; private Label labelHoursActual;
private RichTextBox richTextBoxDescription; private RichTextBox richTextBoxDescription;

View File

@ -25,9 +25,12 @@
public string? Description { get; set; } public string? Description { get; set; }
public string Status { get; set; } = "todo"; public string Status { get; set; } = "todo";
public string StatusName { get; set; } = string.Empty;
public string Priority { get; set; } = "medium"; public string Priority { get; set; } = "medium";
public string PriorityName { get; set; } = string.Empty;
public DateTime? DueDate { get; set; } public DateTime? DueDate { get; set; }
public double? EstimatedHours { get; set; } = 0; public double? EstimatedHours { get; set; } = 0;
@ -36,6 +39,8 @@
public double? HourlyRate { get; set; } = 0; public double? HourlyRate { get; set; } = 0;
public double? Amount { get; set; } = 0;
public string? ParentTaskId { get; set; } public string? ParentTaskId { get; set; }
public DateTime? CreatedAt { get; set; } public DateTime? CreatedAt { get; set; }
@ -50,7 +55,9 @@
Title: {Title} Title: {Title}
Description: {Description} Description: {Description}
Status: {Status} Status: {Status}
StatusName: {StatusName}
Priority: {Priority} Priority: {Priority}
PriorityName: {PriorityName}
DueDate: {DueDate} DueDate: {DueDate}
EstimatedHours: {EstimatedHours} EstimatedHours: {EstimatedHours}
ActualHours: {ActualHours} ActualHours: {ActualHours}
@ -96,6 +103,15 @@
public string? Comment { get; set; } = string.Empty; public string? Comment { get; set; } = string.Empty;
public DateTime? CreatedAt { get; set; } public DateTime? CreatedAt { get; set; }
public override string ToString()
{
return @$"
TaskCommentId: {TaskCommentId}
TaskId: {TaskId}
Comment: {Comment}
CreatedAt: {CreatedAt}
";
}
} }
} }