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 (
-- 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");

View File

@ -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;
}
}

View File

@ -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<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"));
//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)
{

View File

@ -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;

View File

@ -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}
";
}
}
}