diff --git a/Data/TaskData.cs b/Data/TaskData.cs index cf34b8c..062d663 100644 --- a/Data/TaskData.cs +++ b/Data/TaskData.cs @@ -390,7 +390,7 @@ namespace trakker.Data return result; } - public List GetFS() + public List GetFS(string projectId) { var results = new List(); @@ -403,6 +403,8 @@ namespace trakker.Data a.project_id FROM projects a + WHERE + a.project_id = $project_id UNION @@ -414,13 +416,15 @@ namespace trakker.Data b.project_id FROM tasks b + WHERE + b.project_id = $project_id ; "; - using var conn = OpenConnection(); using var cmd = conn.CreateCommand(); cmd.CommandText = sql; + cmd.Parameters.AddWithValue("$project_id", projectId); using var reader = cmd.ExecuteReader(); diff --git a/Forms/MainForm.Designer.cs b/Forms/MainForm.Designer.cs index a909aaa..e0a3f37 100644 --- a/Forms/MainForm.Designer.cs +++ b/Forms/MainForm.Designer.cs @@ -54,8 +54,6 @@ splitContainerTasks2 = new SplitContainer(); dataGridViewProjectTasks = new DataGridView(); tableLayoutPanelTasks2 = new TableLayoutPanel(); - groupBoxTaskDescription = new GroupBox(); - richTextBoxTaskDescription = new RichTextBox(); groupBoxTaskComments = new GroupBox(); richTextBoxTaskComments = new RichTextBox(); MainForm_MenuStrip.SuspendLayout(); @@ -78,7 +76,6 @@ splitContainerTasks2.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)dataGridViewProjectTasks).BeginInit(); tableLayoutPanelTasks2.SuspendLayout(); - groupBoxTaskDescription.SuspendLayout(); groupBoxTaskComments.SuspendLayout(); SuspendLayout(); // @@ -327,47 +324,23 @@ // tableLayoutPanelTasks2.ColumnCount = 1; tableLayoutPanelTasks2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F)); - tableLayoutPanelTasks2.Controls.Add(groupBoxTaskDescription, 0, 0); - tableLayoutPanelTasks2.Controls.Add(groupBoxTaskComments, 0, 1); + tableLayoutPanelTasks2.Controls.Add(groupBoxTaskComments, 0, 0); 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.RowCount = 1; + tableLayoutPanelTasks2.RowStyles.Add(new RowStyle(SizeType.Percent, 100F)); + tableLayoutPanelTasks2.RowStyles.Add(new RowStyle(SizeType.Absolute, 20F)); tableLayoutPanelTasks2.Size = new Size(1230, 303); 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(1224, 69); - 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(1218, 31); - richTextBoxTaskDescription.TabIndex = 0; - richTextBoxTaskDescription.Text = ""; - // // groupBoxTaskComments // groupBoxTaskComments.Controls.Add(richTextBoxTaskComments); groupBoxTaskComments.Dock = DockStyle.Fill; - groupBoxTaskComments.Location = new Point(3, 78); + groupBoxTaskComments.Location = new Point(3, 3); groupBoxTaskComments.Name = "groupBoxTaskComments"; - groupBoxTaskComments.Size = new Size(1224, 222); + groupBoxTaskComments.Size = new Size(1224, 297); groupBoxTaskComments.TabIndex = 1; groupBoxTaskComments.TabStop = false; groupBoxTaskComments.Text = "Comments"; @@ -380,9 +353,10 @@ richTextBoxTaskComments.Location = new Point(3, 35); richTextBoxTaskComments.Name = "richTextBoxTaskComments"; richTextBoxTaskComments.ReadOnly = true; - richTextBoxTaskComments.Size = new Size(1218, 184); + richTextBoxTaskComments.Size = new Size(1218, 259); richTextBoxTaskComments.TabIndex = 1; richTextBoxTaskComments.Text = ""; + richTextBoxTaskComments.LinkClicked += richTextBoxTaskComments_LinkClicked; // // MainForm // @@ -417,7 +391,6 @@ splitContainerTasks2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)dataGridViewProjectTasks).EndInit(); tableLayoutPanelTasks2.ResumeLayout(false); - groupBoxTaskDescription.ResumeLayout(false); groupBoxTaskComments.ResumeLayout(false); ResumeLayout(false); PerformLayout(); @@ -449,8 +422,6 @@ private SplitContainer splitContainerTasks2; private DataGridView dataGridViewProjectTasks; private TableLayoutPanel tableLayoutPanelTasks2; - private GroupBox groupBoxTaskDescription; - private RichTextBox richTextBoxTaskDescription; private GroupBox groupBoxTaskComments; private RichTextBox richTextBoxTaskComments; } diff --git a/Forms/MainForm.cs b/Forms/MainForm.cs index ee3e4ee..a89f8da 100644 --- a/Forms/MainForm.cs +++ b/Forms/MainForm.cs @@ -152,7 +152,14 @@ namespace trakker } public void FillDataGridViewProjects(BindingSource projects) { + int idx = 0; + if (dataGridViewProjects.CurrentRow != null) + { + idx = dataGridViewProjects.CurrentRow.Index; + } + //MessageBox.Show($"Project idx: {idx}", "Debug Info", MessageBoxButtons.OK, MessageBoxIcon.Information); dataGridViewProjects.DataSource = projects; + dataGridViewProjects.Rows[idx].Selected = true; } public void InitDataGridViewProjects() { @@ -292,21 +299,32 @@ namespace trakker } }; - dataGridViewClients.SelectionChanged += (s, e) => + dataGridViewProjects.SelectionChanged += (s, e) => { - if (dataGridViewClients.SelectedRows.Count > 0) + if (_ctrl == null) return; // Safety check to prevent null reference exceptions during initialization) + if (dataGridViewProjects.SelectedRows.Count > 0) { - var selectedClient = dataGridViewClients.SelectedRows[0].DataBoundItem as Client; - if (selectedClient != null) + var selectedProject = dataGridViewProjects.SelectedRows[0].DataBoundItem as Project; + if (selectedProject != null) { - // Handle the selected client as needed - // MessageBox.Show($"Selected Client: {selectedClient.AddressStreet}", "Client Selected", MessageBoxButtons.OK, MessageBoxIcon.Information ); + // Handle the selected project as needed + //MessageBox.Show($"Project ID: {selectedProject.ProjectId}, Selected Project: {selectedProject.ProjectName}", "Project Selected", MessageBoxButtons.OK, MessageBoxIcon.Information ); + _ctrl.LoadTasks(selectedProject.ProjectId); // Load tasks for the selected project + InitProjectTasks(); } } }; } + public void InitProjectTasks() + { + //dataGridViewProjectTasks.Rows.Clear(); + //richTextBoxTaskComments.Text = string.Empty; + richTextBoxTaskComments.WordWrap = false; + richTextBoxTaskComments.ReadOnly = true; + } + public void InitTreeViewTasks() { // Basic TreeView configuration @@ -315,21 +333,33 @@ namespace trakker treeViewTasks1.ShowLines = true; treeViewTasks1.ShowRootLines = true; treeViewTasks1.HideSelection = false; + treeViewTasks1.FullRowSelect = true; treeViewTasks1.EndUpdate(); // When a tree node is clicked, fetch and show notebooks for that folder treeViewTasks1.NodeMouseClick += (sender, e) => { - PTaskFS? selectedNode = (PTaskFS?)e.Node.Tag ?? new PTaskFS(); - if (selectedNode?.Parent == "/" || selectedNode?.GUID == "/") - { - _ctrl.LoadTasksRecursive(selectedNode.ProjectId, PTask.RecursiveRoot.PARENT_TASK_ID); // Load all tasks for the project when root node is clicked - return; - } - _ctrl.LoadTasksRecursive(selectedNode?.GUID ?? "/", PTask.RecursiveRoot.TASK_ID); // Load all tasks for the project when root node is clicked + TreeViewTasks1_NodeMouseClick(e.Node, e.Button); }; } + public void TreeViewTasks1_NodeMouseClick(TreeNode node, MouseButtons button = MouseButtons.Left) + { + if (node == null) return; + + // Put all your NodeMouseClick logic here + treeViewTasks1.SelectedNode = node; // Usually what you want + node.EnsureVisible(); + + // ... your other code (open form, load data, etc.) + PTaskFS? selectedNode = (PTaskFS?)node.Tag ?? new PTaskFS(); + if (selectedNode?.Parent == "/" || selectedNode?.GUID == "/") + { + _ctrl.LoadTasksRecursive(selectedNode.ProjectId, PTask.RecursiveRoot.PARENT_TASK_ID); // Load all tasks for the project when root node is clicked + return; + } + _ctrl.LoadTasksRecursive(selectedNode?.GUID ?? "/", PTask.RecursiveRoot.TASK_ID); // Load all tasks for the project when root node is clicked + } public void FillTreeViewTasks(List items) { if (items == null) return; @@ -340,7 +370,7 @@ namespace trakker // Root node PTaskFS root = new(); root.GUID = "/"; - root.Node = "/Projects"; + root.Node = "/Project"; root.Parent = string.Empty; TreeNode rootNode = new TreeNode(root.Node) { Tag = root }; treeViewTasks1.Nodes.Add(rootNode); @@ -372,7 +402,7 @@ namespace trakker { var text = string.IsNullOrWhiteSpace(child.Node) ? "(unnamed)" : child.Node!.Trim(); var tn = new TreeNode(text) { Tag = child }; - //tn.Expand(); + tn.Expand(); parentNode.Nodes.Add(tn); // Recurse using this child's node text as the parent key @@ -383,9 +413,13 @@ namespace trakker // // Start recursion from root key AddChildren(rootNode, "/"); - //rootNode.ExpandAll(); - rootNode.Expand(); // Expand first level for better UX + rootNode.ExpandAll(); treeViewTasks1.EndUpdate(); // End the update + + TreeNode node = rootNode.FirstNode; + TreeViewTasks1_NodeMouseClick(node, MouseButtons.Left); + //treeViewTasks1.SelectedNode = node; + //treeViewTasks1.Focus(); } private void addTaskSubtaskToolStripMenuItem_Click(object sender, EventArgs e) @@ -416,7 +450,7 @@ namespace trakker try { taskData.Upsert(task); - treeViewTasks1.SelectedNode.Nodes.Add(new TreeNode(task.Title ?? "") { Tag = new PTaskFS { GUID = task.TaskId ?? "", Node = task.Title ?? "", Parent = task.ParentTaskId ?? "", HourlyRate = task.HourlyRate, ProjectId = task.ProjectId ?? "" } }); + treeViewTasks1.SelectedNode.Nodes.Add(new TreeNode(task.Title ?? "") { Tag = new PTaskFS { GUID = task.TaskId ?? "", Node = task.Title ?? "", Parent = task.ParentTaskId ?? "", HourlyRate = task.HourlyRate, ProjectId = task.ProjectId ?? "" } }); //_ctrl.LoadTasks(); } catch (Exception ex) @@ -649,6 +683,48 @@ namespace trakker { dataGridViewProjectTasks_SelectionChanged(s, e); }; + + dataGridViewProjectTasks.DoubleClick += (s, e) => + { // BCN + var projectIdx = dataGridViewProjects.SelectedRows[0].Index; + var taskIdx = dataGridViewProjectTasks.SelectedRows[0].Index; + + var row = dataGridViewProjectTasks.SelectedRows[0]; + if (row != null) + { + var task = dataGridViewProjectTasks.SelectedRows[0].DataBoundItem as PTask; + TaskForm dialog = new TaskForm(task!, _ctrl.GetLOV("task.status"), _ctrl.GetLOV("task.priority")); + DialogResult result = dialog.ShowDialog(this); + if (result == DialogResult.OK) + { + task = dialog.Task; + try + { + TaskData taskData = new TaskData(connectionString); + taskData.Upsert(task); + //_ctrl.LoadTasks(task.ProjectId ?? string.Empty); // Reload tasks to update the DataGridView with any changes + _ctrl.LoadProjects(); // Reload projects to update the DataGridView with any changes + _ctrl.LoadTasksRecursive(task.TaskId ?? string.Empty, PTask.RecursiveRoot.TASK_ID); // Reload tasks to update the tree view with any changes + + dataGridViewProjects.ClearSelection(); + if (projectIdx >= 0 && projectIdx < dataGridViewProjects.Rows.Count) + { + dataGridViewProjects.Rows[projectIdx].Selected = true; + } + dataGridViewProjectTasks.ClearSelection(); + if (taskIdx >= 0 && taskIdx < dataGridViewProjectTasks.Rows.Count) + { + dataGridViewProjectTasks.Rows[taskIdx].Selected = true; + } + } + catch (Exception ex) + { + MessageBox.Show($"Error saving task: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + } + }; } public void dataGridViewProjectTasks_SelectionChanged(object? sender, EventArgs? e) { @@ -658,7 +734,6 @@ namespace trakker 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(); @@ -671,9 +746,26 @@ namespace trakker } public void FillDataGridViewProjectTasks(BindingSource tasks) { + //dataGridViewProjectTasks.Rows.Clear(); dataGridViewProjectTasks.DataSource = tasks; - dataGridViewProjectTasks.Refresh(); + //dataGridViewProjectTasks.Refresh(); } + private void richTextBoxTaskComments_LinkClicked(object sender, LinkClickedEventArgs e) + { + try + { + // Open the URL in the default browser + System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo + { + FileName = e.LinkText, + UseShellExecute = true + }); + } + catch (Exception ex) + { + DialogExtensions.GenericError(String.Format($"Failed to open link: {ex.Message}")); + } + } } } diff --git a/Interfaces/IMainForm.cs b/Interfaces/IMainForm.cs index c900f40..ab8c38f 100644 --- a/Interfaces/IMainForm.cs +++ b/Interfaces/IMainForm.cs @@ -16,6 +16,7 @@ namespace trakker.Interfaces void FillTreeViewTasks(List items); void InitTreeViewTasks(); void InitDataGridViewProjectTasks(); + void InitProjectTasks(); void FillDataGridViewProjectTasks(BindingSource tasks); } } diff --git a/Services/MainCtrl.cs b/Services/MainCtrl.cs index f65d9e0..eea7a6c 100644 --- a/Services/MainCtrl.cs +++ b/Services/MainCtrl.cs @@ -59,12 +59,14 @@ namespace trakker.Services var x = project.Status; } _view.FillDataGridViewProjects(new BindingSource { DataSource = projects }); - LoadTasks(); + //LoadTasks(); } - internal void LoadTasks() + internal void LoadTasks(string projectId) { var dbo = new TaskData(_connectionString); - _view.FillTreeViewTasks(dbo.GetFS()); + _view.FillTreeViewTasks(dbo.GetFS(projectId)); + _view.InitProjectTasks(); + //_view.InitDataGridViewProjectTasks(); } internal void LoadTasksRecursive(string id, PTask.RecursiveRoot root) {