799 lines
35 KiB
C#
799 lines
35 KiB
C#
using Microsoft.Data.Sqlite;
|
|
using System.ComponentModel;
|
|
|
|
using trakker.Data;
|
|
using trakker.Forms;
|
|
using trakker.Interfaces;
|
|
using trakker.Models;
|
|
using trakker.Services;
|
|
using trakker.Utilities;
|
|
|
|
namespace trakker
|
|
{
|
|
public partial class MainForm : Form, IMainForm
|
|
{
|
|
private const string _AppVersion = "[n.n.n]";
|
|
private static readonly string _empty = string.Empty;
|
|
private string _ConnectionString = _empty;
|
|
readonly MainCtrl _ctrl;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="MainForm"/> class.
|
|
/// Sets up the form's controls and event handlers by calling
|
|
/// <see cref="InitializeComponent"/> which is generated by the designer.
|
|
/// </summary>
|
|
public MainForm()
|
|
{
|
|
InitializeComponent();
|
|
|
|
// build connection string that will be used for database connections
|
|
// ------------------------------------------------------------------------
|
|
var dbPath = Path.Combine(AppContext.BaseDirectory, "trakker.db");
|
|
_ConnectionString = new SqliteConnectionStringBuilder
|
|
{
|
|
DataSource = dbPath,
|
|
Mode = SqliteOpenMode.ReadWriteCreate,
|
|
Cache = SqliteCacheMode.Shared
|
|
}.ToString();
|
|
|
|
Text = $"Project Trakker - v{_AppVersion} / pragmattica.com";
|
|
tabControlMainForm.TabPages[0].Text = " Home ";
|
|
tabControlMainForm.TabPages[1].Text = " Clients ";
|
|
tabControlMainForm.TabPages[2].Text = " Projects ";
|
|
|
|
_ctrl = new Services.MainCtrl(this, _ConnectionString);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles the Click event of the Exit menu item. When invoked, this
|
|
/// method terminates the application.
|
|
/// </summary>
|
|
/// <param name="sender">The source of the event.</param>
|
|
/// <param name="e">Event data associated with the click event.</param>
|
|
private void MainForm_Exit_MenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
Application.Exit();
|
|
}
|
|
|
|
public void Client_DataGridViewClients_Init(BindingList<Client> clients)
|
|
{
|
|
dataGridViewClients.AllowUserToAddRows = true;
|
|
dataGridViewClients.AllowUserToDeleteRows = true;
|
|
dataGridViewClients.AutoGenerateColumns = false;
|
|
dataGridViewClients.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
|
|
dataGridViewClients.BackgroundColor = Color.White;
|
|
dataGridViewClients.ColumnHeadersVisible = true;
|
|
dataGridViewClients.DataSource = clients;
|
|
dataGridViewClients.MultiSelect = false;
|
|
dataGridViewClients.RowHeadersVisible = false;
|
|
dataGridViewClients.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
|
|
|
|
dataGridViewClients.Columns.Clear();
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
|
DataPropertyName = "Name",
|
|
Name = "Name",
|
|
Visible = true,
|
|
};
|
|
dataGridViewClients.Columns.Add(textColumn);
|
|
}
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
|
DataPropertyName = "Company",
|
|
Name = "Company",
|
|
Visible = true,
|
|
};
|
|
dataGridViewClients.Columns.Add(textColumn);
|
|
}
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
|
DataPropertyName = "Email",
|
|
Name = "Email",
|
|
Visible = true,
|
|
};
|
|
dataGridViewClients.Columns.Add(textColumn);
|
|
}
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
|
DataPropertyName = "Phone",
|
|
Name = "Phone",
|
|
Visible = true,
|
|
};
|
|
dataGridViewClients.Columns.Add(textColumn);
|
|
}
|
|
|
|
dataGridViewClients.DoubleClick += (s, e) =>
|
|
{
|
|
Client_DataGridViewClients_DoubleClick(s!, e);
|
|
};
|
|
|
|
dataGridViewClients.SelectionChanged += (s, e) =>
|
|
{
|
|
Client_DataGridViewClients_SelectionChanged(s!, e);
|
|
};
|
|
|
|
}
|
|
|
|
public void Client_DataGridViewClients_DoubleClick(object sender, EventArgs e)
|
|
{
|
|
if (dataGridViewClients.SelectedRows.Count > 0)
|
|
{
|
|
var selectedClient = dataGridViewClients.SelectedRows[0].DataBoundItem as Client;
|
|
if (selectedClient != null)
|
|
{
|
|
var dialog = new ClientForm(selectedClient, _ctrl.GetLOV("state"));
|
|
if (dialog.ShowDialog(this) == DialogResult.OK)
|
|
{
|
|
Client client = dialog.Client;
|
|
ClientData clientData = new ClientData(_ConnectionString);
|
|
try
|
|
{
|
|
clientData.Upsert(client);
|
|
dataGridViewClients.Refresh(); // Refresh the DataGridView to reflect changes
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show($"Error saving client: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Client_DataGridViewClients_SelectionChanged(object sender, EventArgs 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 Project_DataGridViewProjects_Fill(BindingSource projects)
|
|
{
|
|
int idx = 0;
|
|
if (dataGridViewProjects.CurrentRow != null)
|
|
{
|
|
idx = dataGridViewProjects.CurrentRow.Index;
|
|
}
|
|
dataGridViewProjects.DataSource = projects;
|
|
dataGridViewProjects.Rows[idx].Selected = true;
|
|
}
|
|
public void Project_DataGridViewProjects_Init()
|
|
{
|
|
dataGridViewProjects.AllowUserToAddRows = true;
|
|
dataGridViewProjects.AllowUserToDeleteRows = true;
|
|
dataGridViewProjects.AutoGenerateColumns = false;
|
|
dataGridViewProjects.BackgroundColor = Color.White;
|
|
dataGridViewProjects.ColumnHeadersVisible = true;
|
|
dataGridViewProjects.MultiSelect = false;
|
|
dataGridViewProjects.RowHeadersVisible = false;
|
|
dataGridViewProjects.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
|
|
|
|
dataGridViewProjects.Columns.Clear();
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.None,
|
|
DataPropertyName = "ProjectName",
|
|
Name = "Project Name",
|
|
Visible = true,
|
|
Width = 350,
|
|
};
|
|
dataGridViewProjects.Columns.Add(textColumn);
|
|
}
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
|
DataPropertyName = "ClientName",
|
|
Name = "Client Name",
|
|
Visible = true,
|
|
};
|
|
dataGridViewProjects.Columns.Add(textColumn);
|
|
}
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
|
DataPropertyName = "StartDate",
|
|
DefaultCellStyle = { Format = "MM/dd/yyyy" },
|
|
Name = "Start Date",
|
|
Visible = true,
|
|
};
|
|
dataGridViewProjects.Columns.Add(textColumn);
|
|
}
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
|
DataPropertyName = "EndDate",
|
|
DefaultCellStyle = { Format = "MM/dd/yyyy" },
|
|
Name = "End Date",
|
|
Visible = true,
|
|
};
|
|
dataGridViewProjects.Columns.Add(textColumn);
|
|
}
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
|
DataPropertyName = "HourlyRate",
|
|
Name = "Hourly Rate",
|
|
Visible = true,
|
|
};
|
|
textColumn.DefaultCellStyle.Format = "$#,##0.00";
|
|
textColumn.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
|
|
textColumn.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleRight;
|
|
dataGridViewProjects.Columns.Add(textColumn);
|
|
}
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
|
DataPropertyName = "Budget",
|
|
Name = "Budget",
|
|
Visible = true,
|
|
};
|
|
textColumn.DefaultCellStyle.Format = "$#,##0.00";
|
|
textColumn.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
|
|
textColumn.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleRight;
|
|
dataGridViewProjects.Columns.Add(textColumn);
|
|
}
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
|
DataPropertyName = "Actuals",
|
|
Name = "Actuals",
|
|
Visible = true,
|
|
};
|
|
textColumn.DefaultCellStyle.Format = "$#,##0.00";
|
|
textColumn.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
|
|
textColumn.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleRight;
|
|
dataGridViewProjects.Columns.Add(textColumn);
|
|
}
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
|
DataPropertyName = "StatusName",
|
|
Name = "Status",
|
|
Visible = true,
|
|
};
|
|
dataGridViewProjects.Columns.Add(textColumn);
|
|
}
|
|
|
|
dataGridViewProjects.DoubleClick += (s, e) =>
|
|
{
|
|
Project_DataGridViewProjects_DoubleClick(s!, e);
|
|
};
|
|
|
|
dataGridViewProjects.SelectionChanged += (s, e) =>
|
|
{
|
|
Project_DataGridViewProjects_SelectionChanged(s!, e);
|
|
};
|
|
|
|
}
|
|
|
|
public void Project_DataGridViewProjects_DoubleClick(object sender, EventArgs e)
|
|
{
|
|
if (dataGridViewProjects.SelectedRows.Count > 0)
|
|
{
|
|
var selectedIdx = dataGridViewProjects.SelectedRows[0].Index;
|
|
var selectedProject = dataGridViewProjects.SelectedRows[0].DataBoundItem as Project;
|
|
if (selectedProject != null)
|
|
{
|
|
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
|
|
dataGridViewProjects.ClearSelection();
|
|
if (selectedIdx >= 0 && selectedIdx < dataGridViewProjects.Rows.Count)
|
|
{
|
|
dataGridViewProjects.Rows[selectedIdx].Selected = true;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show($"Error saving project: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Project_DataGridViewProjects_SelectionChanged(object sender, EventArgs e)
|
|
{
|
|
if (_ctrl == null) return; // Safety check to prevent null reference exceptions during initialization)
|
|
if (dataGridViewProjects.SelectedRows.Count > 0)
|
|
{
|
|
var selectedProject = dataGridViewProjects.SelectedRows[0].DataBoundItem as Project;
|
|
if (selectedProject != null)
|
|
{
|
|
// 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 PTask_TreeViewTasks1_Init()
|
|
{
|
|
// Basic TreeView configuration
|
|
treeViewTasks1.BeginUpdate();
|
|
treeViewTasks1.Nodes.Clear();
|
|
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) =>
|
|
{
|
|
PTask_TreeViewTasks1_NodeMouseClick(e.Node, e.Button);
|
|
};
|
|
|
|
}
|
|
public void PTask_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 PTask_TreeViewTasks1_Fill(List<PTaskFS> items)
|
|
{
|
|
if (items == null) return;
|
|
|
|
treeViewTasks1.BeginUpdate(); // Improves performance for large trees
|
|
treeViewTasks1.Nodes.Clear();
|
|
|
|
// Root node
|
|
PTaskFS root = new();
|
|
root.GUID = "/";
|
|
root.Node = "/Project";
|
|
root.Parent = _empty;
|
|
TreeNode rootNode = new TreeNode(root.Node) { Tag = root };
|
|
treeViewTasks1.Nodes.Add(rootNode);
|
|
|
|
// Normalize keys so null/empty parents map to root key "/"
|
|
static string NormalizeKey(string? k) => string.IsNullOrWhiteSpace(k) ? "/" : k!.Trim();
|
|
|
|
// Build lookup: parentKey -> list of children
|
|
var lookup = new Dictionary<string, List<PTaskFS>>();
|
|
foreach (var it in items)
|
|
{
|
|
var key = NormalizeKey(it.Parent);
|
|
if (!lookup.TryGetValue(key, out var list))
|
|
{
|
|
list = new List<PTaskFS>();
|
|
lookup[key] = list;
|
|
}
|
|
list.Add(it);
|
|
}
|
|
|
|
var keys = lookup.Keys.ToList();
|
|
// Recursive adder: attach child nodes to a parent TreeNode.
|
|
// Uses normalized keys so null/empty parents become "/".
|
|
void AddChildren(TreeNode parentNode, string parentKey)
|
|
{
|
|
if (!lookup.TryGetValue(parentKey, out var children)) return;
|
|
// Optional: sort children by Node text for deterministic order
|
|
foreach (var child in children.OrderBy(c => c.Node))
|
|
{
|
|
var text = string.IsNullOrWhiteSpace(child.Node) ? "(unnamed)" : child.Node!.Trim();
|
|
var tn = new TreeNode(text) { Tag = child };
|
|
tn.Expand();
|
|
parentNode.Nodes.Add(tn);
|
|
|
|
// Recurse using this child's node text as the parent key
|
|
AddChildren(tn, NormalizeKey(child.GUID));
|
|
}
|
|
}
|
|
|
|
// // Start recursion from root key
|
|
AddChildren(rootNode, "/");
|
|
|
|
rootNode.ExpandAll();
|
|
treeViewTasks1.EndUpdate(); // End the update
|
|
|
|
TreeNode node = rootNode.FirstNode;
|
|
PTask_TreeViewTasks1_NodeMouseClick(node, MouseButtons.Left);
|
|
//treeViewTasks1.SelectedNode = node;
|
|
//treeViewTasks1.Focus();
|
|
}
|
|
|
|
private void PTask_AddTaskSubtaskToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
if (treeViewTasks1.SelectedNode == null) return;
|
|
PTaskFS? selectedNode = (PTaskFS?)treeViewTasks1.SelectedNode.Tag ?? new PTaskFS();
|
|
if (selectedNode?.Parent == "")
|
|
{
|
|
MessageBox.Show("Cannot add tasks to root node", "Add Task", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
return;
|
|
}
|
|
string taskId = Guid.NewGuid().ToString();
|
|
PTask task = new()
|
|
{
|
|
TaskId = taskId,
|
|
Title = "New Task",
|
|
Description = _empty,
|
|
ParentTaskId = selectedNode?.GUID == "/" ? null : selectedNode?.GUID,
|
|
HourlyRate = selectedNode?.HourlyRate,
|
|
ProjectId = selectedNode?.ProjectId, // Root node has no project
|
|
};
|
|
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;
|
|
TaskData taskData = new TaskData(_ConnectionString);
|
|
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 ?? "" } });
|
|
//_ctrl.LoadTasks();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show($"Error saving task: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PTask_EditThisTaskSubtaskToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
if (treeViewTasks1.SelectedNode == null) return;
|
|
PTaskFS? selectedTask = treeViewTasks1.SelectedNode.Tag as PTaskFS;
|
|
if (selectedTask?.Parent == "/" || selectedTask?.GUID == "/")
|
|
{
|
|
MessageBox.Show("Cannot edit root node", "Edit Task", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
return;
|
|
}
|
|
TaskData taskData = new TaskData(_ConnectionString);
|
|
PTask task = taskData.Get(selectedTask?.GUID);
|
|
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;
|
|
//MessageBox.Show(task.ToString(), "Task Details", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
try
|
|
{
|
|
taskData.Upsert(task);
|
|
treeViewTasks1.SelectedNode.Text = task.Title ?? ""; // Update the node text in the tree view
|
|
_ctrl.LoadTasksRecursive(selectedTask!.GUID, PTask.RecursiveRoot.TASK_ID); // Reload tasks to update the tree view with any changes
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show($"Error saving task: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PTask_DeleteThisTaskSubtaskToolStripMenuItem_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 delete root node", "Delete Task", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
return;
|
|
}
|
|
if (treeViewTasks1.SelectedNode.Nodes.Count > 0)
|
|
{
|
|
MessageBox.Show("Cannot delete a task with subtasks", "Delete Task", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
return;
|
|
}
|
|
TaskData taskData = new TaskData(_ConnectionString);
|
|
DialogResult result = MessageBox.Show("Are you sure you want to delete this task?", "Delete Task", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
|
|
if (result == DialogResult.Yes)
|
|
{
|
|
//MessageBox.Show(task.ToString(), "Task Details", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
try
|
|
{
|
|
taskData.Delete(selectedTask?.GUID ?? _empty);
|
|
treeViewTasks1.SelectedNode.Remove();
|
|
//_ctrl.LoadTasks(); // Reload tasks to update the DataGridView with any changes
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show($"Error deleting task: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PTask_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 Comment");
|
|
DialogResult result = textAreaForm.ShowDialog(this);
|
|
if (result == DialogResult.OK)
|
|
{
|
|
if (string.IsNullOrEmpty(textAreaForm.BasicText)) return;
|
|
TaskData taskData = new TaskData(_ConnectionString);
|
|
try
|
|
{
|
|
PTaskComment comment = new()
|
|
{
|
|
TaskId = selectedTask?.GUID ?? _empty,
|
|
Comment = textAreaForm.BasicText ?? _empty,
|
|
};
|
|
taskData.SaveComment(comment);
|
|
DialogExtensions.GenericSuccess($"Comment saved successfully for task '{selectedTask!.Node}'.");
|
|
PTask_DataGridViewProjectTasks_SelectionChanged(null, null);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show($"Error deleting task: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
}
|
|
public void PTask_DataGridViewProjectTasks_Init()
|
|
{
|
|
dataGridViewProjectTasks.AllowUserToAddRows = false;
|
|
dataGridViewProjectTasks.AllowUserToDeleteRows = false;
|
|
dataGridViewProjectTasks.AutoGenerateColumns = false;
|
|
dataGridViewProjectTasks.BackgroundColor = Color.White;
|
|
dataGridViewProjectTasks.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft;
|
|
dataGridViewProjectTasks.ColumnHeadersDefaultCellStyle.WrapMode = DataGridViewTriState.False;
|
|
dataGridViewProjectTasks.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
|
dataGridViewProjectTasks.ColumnHeadersVisible = true;
|
|
dataGridViewProjectTasks.MultiSelect = false;
|
|
dataGridViewProjectTasks.ReadOnly = true;
|
|
dataGridViewProjectTasks.RowHeadersVisible = false;
|
|
dataGridViewProjectTasks.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
|
|
|
|
// Then add your columns...
|
|
dataGridViewProjectTasks.Columns.Clear();
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.None,
|
|
DataPropertyName = "Title",
|
|
Name = "Title",
|
|
Visible = true,
|
|
Width = 350,
|
|
};
|
|
dataGridViewProjectTasks.Columns.Add(textColumn);
|
|
}
|
|
// 1. Status - Most important for quick visual scanning
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
|
DataPropertyName = "StatusName",
|
|
Name = "Status",
|
|
Visible = true,
|
|
};
|
|
dataGridViewProjectTasks.Columns.Add(textColumn);
|
|
}
|
|
|
|
// 2. Priority - Right after status
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
|
DataPropertyName = "PriorityName",
|
|
Name = "Priority",
|
|
Visible = true,
|
|
};
|
|
dataGridViewProjectTasks.Columns.Add(textColumn);
|
|
}
|
|
|
|
// 3. Due Date - Time sensitivity
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
|
DataPropertyName = "DueDate",
|
|
DefaultCellStyle = { Format = "MM/dd/yyyy" },
|
|
Name = "Due Date",
|
|
Visible = true,
|
|
};
|
|
dataGridViewProjectTasks.Columns.Add(textColumn);
|
|
}
|
|
|
|
// 4. Estimated Hours
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
|
DataPropertyName = "EstimatedHours",
|
|
Name = "Est Hours",
|
|
Visible = true,
|
|
};
|
|
textColumn.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
|
|
textColumn.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleRight;
|
|
dataGridViewProjectTasks.Columns.Add(textColumn);
|
|
}
|
|
|
|
// 5. Actual Hours
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
|
DataPropertyName = "ActualHours",
|
|
Name = "Act Hours",
|
|
Visible = true,
|
|
};
|
|
textColumn.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
|
|
textColumn.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleRight;
|
|
dataGridViewProjectTasks.Columns.Add(textColumn);
|
|
}
|
|
|
|
// 6. Hourly Rate - Financial info at the end
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
|
DataPropertyName = "HourlyRate",
|
|
Name = "Hourly Rate",
|
|
Visible = true,
|
|
};
|
|
textColumn.DefaultCellStyle.Format = "$#,##0.00";
|
|
textColumn.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
|
|
textColumn.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleRight;
|
|
dataGridViewProjectTasks.Columns.Add(textColumn);
|
|
}
|
|
|
|
// 7. Amount - Financial info at the end
|
|
{
|
|
var textColumn = new DataGridViewTextBoxColumn
|
|
{
|
|
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) =>
|
|
{
|
|
PTask_DataGridViewProjectTasks_SelectionChanged(s, e);
|
|
};
|
|
|
|
dataGridViewProjectTasks.DoubleClick += (s, e) =>
|
|
{
|
|
PTask_DataGridViewProjectTasks_DoubleClick(s, e);
|
|
};
|
|
}
|
|
public void PTask_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)
|
|
{
|
|
TaskData taskData = new TaskData(_ConnectionString);
|
|
List<PTaskComment> comments = taskData.GetComments(selectedTask.TaskId ?? _empty);
|
|
richTextBoxTaskComments.Clear();
|
|
foreach (var comment in comments)
|
|
{
|
|
richTextBoxTaskComments.AppendText($"{comment.CreatedAt}\n---\n{comment.Comment}\n\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void PTask_DataGridViewProjectTasks_DoubleClick(object? sender, EventArgs? e)
|
|
{
|
|
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 ?? _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 PTask_DataGridViewProjectTasks_Fill(BindingSource tasks)
|
|
{
|
|
//dataGridViewProjectTasks.Rows.Clear();
|
|
dataGridViewProjectTasks.DataSource = tasks;
|
|
//dataGridViewProjectTasks.Refresh();
|
|
}
|
|
|
|
private void PTask_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}"));
|
|
}
|
|
}
|
|
}
|
|
}
|