diff --git a/Data/ClientData.cs b/Data/ClientData.cs
index ffbc6a5..a00e964 100644
--- a/Data/ClientData.cs
+++ b/Data/ClientData.cs
@@ -225,44 +225,6 @@ namespace trakker.Data
return result;
}
- ///
- /// Executes arbitrary, ad-hoc SQL against the database inside a transaction.
- ///
- /// A SQL statement to execute. The caller is responsible for
- /// ensuring the SQL is safe and properly parameterized to avoid SQL injection.
- ///
- /// This method is intended for one-off maintenance or administrative commands.
- /// It does not return any result; if a scalar value is produced by the SQL,
- /// the current implementation captures it but does not expose it to the caller.
- ///
- public void Adhoc(string sql)
- {
- using var conn = OpenConnection();
- using var tx = conn.BeginTransaction();
-
- int? result = 0;
- try
- {
- using (var cmd = conn.CreateCommand())
- {
- cmd.Transaction = tx;
- cmd.CommandText = sql;
- cmd.ExecuteNonQuery();
- }
-
- using var idCmd = conn.CreateCommand();
- idCmd.Transaction = tx;
- result = (int?)idCmd.ExecuteScalar() ;
-
- tx.Commit();
- }
- catch
- {
- tx.Rollback();
- throw;
- }
-
- }
}
}
diff --git a/Data/DataAccess.cs b/Data/DataAccess.cs
index 2c02f52..f111997 100644
--- a/Data/DataAccess.cs
+++ b/Data/DataAccess.cs
@@ -25,5 +25,44 @@ namespace trakker.Data
conn.Open();
return conn;
}
+
+ ///
+ /// Executes arbitrary, ad-hoc SQL against the database inside a transaction.
+ ///
+ /// A SQL statement to execute. The caller is responsible for
+ /// ensuring the SQL is safe and properly parameterized to avoid SQL injection.
+ ///
+ /// This method is intended for one-off maintenance or administrative commands.
+ /// It does not return any result; if a scalar value is produced by the SQL,
+ /// the current implementation captures it but does not expose it to the caller.
+ ///
+ public void Adhoc(string sql)
+ {
+ using var conn = OpenConnection();
+ using var tx = conn.BeginTransaction();
+
+ int? result = 0;
+ try
+ {
+ using (var cmd = conn.CreateCommand())
+ {
+ cmd.Transaction = tx;
+ cmd.CommandText = sql;
+ cmd.ExecuteNonQuery();
+ }
+
+ using var idCmd = conn.CreateCommand();
+ idCmd.Transaction = tx;
+ result = (int?)idCmd.ExecuteScalar() ;
+
+ tx.Commit();
+ }
+ catch
+ {
+ tx.Rollback();
+ throw;
+ }
+
+ }
}
}
diff --git a/Data/ProjectData.cs b/Data/ProjectData.cs
new file mode 100644
index 0000000..589ffe4
--- /dev/null
+++ b/Data/ProjectData.cs
@@ -0,0 +1,232 @@
+using System.ComponentModel;
+using trakker.Models;
+
+namespace trakker.Data
+{
+ ///
+ /// Provides data access methods for the entity.
+ /// This class encapsulates database operations such as upsert, delete and ad-hoc
+ /// SQL execution for projects. It inherits from which
+ /// provides connection management.
+ ///
+ internal class ProjectData(string connectionString) : DataAccess(connectionString)
+ {
+ public BindingList Get(string? projectId = null)
+ {
+ var results = new BindingList();
+
+ string whereClause = "1 = 1";
+ if (projectId != null)
+ {
+ whereClause = "p.project_id = $project_id";
+ }
+
+ string sql = $@"
+ SELECT
+ p.project_id,
+ p.client_id,
+ p.project_code,
+ c.name AS client_name,
+ p.name AS project_name,
+ p.description,
+ p.start_date,
+ p.end_date,
+ p.budget,
+ p.status,
+ p.hourly_rate,
+ p.notes,
+ p.created_at,
+ p.updated_at
+ FROM projects p
+ LEFT JOIN clients c ON p.client_id = c.client_id
+ WHERE p.status = 'active'
+ AND {whereClause}
+ ORDER BY p.start_date DESC, p.name ASC;
+ ;
+ ";
+
+ using var conn = OpenConnection();
+ using var cmd = conn.CreateCommand();
+ cmd.CommandText = sql;
+
+ if (projectId != null)
+ {
+ cmd.Parameters.AddWithValue("$project_id", projectId);
+ }
+ using var reader = cmd.ExecuteReader();
+
+ var _var1 = reader.GetOrdinal("project_id");
+ var _var2 = reader.GetOrdinal("client_id");
+ var _var3 = reader.GetOrdinal("project_code");
+ var _var4 = reader.GetOrdinal("client_name");
+ var _var5 = reader.GetOrdinal("project_name");
+ var _var6 = reader.GetOrdinal("description");
+ var _var7 = reader.GetOrdinal("start_date");
+ var _var8 = reader.GetOrdinal("end_date");
+ var _var9 = reader.GetOrdinal("budget");
+ var _var10 = reader.GetOrdinal("status");
+ var _var11 = reader.GetOrdinal("hourly_rate");
+ var _var12 = reader.GetOrdinal("notes");
+ var _var13 = reader.GetOrdinal("created_at");
+ var _var14 = reader.GetOrdinal("updated_at");
+
+ while (reader.Read())
+ {
+ results.Add(new Project
+ {
+ ProjectId = reader.GetString(_var1),
+ ClientId = reader.GetString(_var2),
+ ProjectCode = reader.GetString(_var3),
+ ClientName = reader.GetString(_var4),
+ ProjectName = reader.GetString(_var5),
+ Description = reader.GetString(_var6),
+ StartDate = reader.IsDBNull(_var7) ? null : reader.GetDateTime(_var7),
+ EndDate = reader.IsDBNull(_var8) ? null : reader.GetDateTime(_var8),
+ Budget = reader.GetDecimal(_var9),
+ Status = reader.GetString(_var10),
+ HourlyRate = reader.GetDecimal(_var11),
+ Notes = reader.GetString(_var12),
+ CreatedAt = reader.GetDateTime(_var13),
+ UpdatedAt = reader.GetDateTime(_var14),
+ });
+ }
+
+ return results;
+ }
+
+ ///
+ /// Inserts a new project record or updates an existing one (upsert) using
+ /// the provided model. This method executes
+ /// a single SQL statement inside a transaction and will commit on
+ /// success or roll back on failure.
+ ///
+ /// The model to insert or update. Must not be null.
+ ///
+ /// The SQL statement uses an ON CONFLICT clause to perform the update when a
+ /// matching project_id already exists. Parameter names correspond to the
+ /// project model property names.
+ ///
+ public void Upsert(Project project)
+ {
+ const string sql = @"
+ INSERT INTO projects (
+ project_id,
+ client_id,
+ name,
+ description,
+ start_date,
+ end_date,
+ budget,
+ status,
+ hourly_rate,
+ notes
+ )
+ VALUES (
+ $project_id,
+ $client_id,
+ $name,
+ $description,
+ $start_date,
+ $end_date,
+ $budget,
+ $status,
+ $hourly_rate,
+ $notes
+ )
+ ON CONFLICT (project_id) DO UPDATE SET
+ client_id = excluded.client_id,
+ name = excluded.name,
+ description = excluded.description,
+ start_date = excluded.start_date,
+ end_date = excluded.end_date,
+ budget = excluded.budget,
+ status = excluded.status,
+ hourly_rate = excluded.hourly_rate,
+ notes = excluded.notes,
+ updated_at = CURRENT_TIMESTAMP;
+ ";
+
+ using var conn = OpenConnection();
+ using var tx = conn.BeginTransaction();
+
+ try
+ {
+ using (var cmd = conn.CreateCommand())
+ {
+ cmd.Transaction = tx;
+ cmd.CommandText = sql;
+ cmd.Parameters.AddWithValue("$project_id", project.ProjectId);
+ cmd.Parameters.AddWithValue("$client_id", project.ClientId);
+ cmd.Parameters.AddWithValue("$name", project.ProjectName);
+ cmd.Parameters.AddWithValue("$description", project.Description);
+ cmd.Parameters.AddWithValue("$start_date", project.StartDate);
+ cmd.Parameters.AddWithValue("$end_date", project.EndDate);
+ cmd.Parameters.AddWithValue("$budget", project.Budget);
+ cmd.Parameters.AddWithValue("$status", project.Status);
+ cmd.Parameters.AddWithValue("$hourly_rate", project.HourlyRate);
+ cmd.Parameters.AddWithValue("$notes", project.Notes);
+ cmd.ExecuteNonQuery();
+ }
+
+ tx.Commit();
+ }
+ catch
+ {
+ tx.Rollback();
+ throw;
+ }
+
+ }
+ ///
+ /// Deletes the project with the specified from the
+ /// database.
+ ///
+ /// The identifier of the project to delete.
+ /// An optional integer representing any scalar value returned by the
+ /// command executed after deletion (if applicable). May be null.
+ ///
+ /// The method executes within a transaction. The current implementation attempts
+ /// to read a scalar value after the delete; that value depends on surrounding
+ /// database triggers or commands and may be null.
+ ///
+ public int? Delete(string projectId)
+ {
+ const string sql = @"
+ DELETE FROM
+ projects
+ WHERE
+ project_id = $project_id
+ ;
+ ";
+
+ using var conn = OpenConnection();
+ using var tx = conn.BeginTransaction();
+
+ int? result = 0;
+ try
+ {
+ using (var cmd = conn.CreateCommand())
+ {
+ cmd.Transaction = tx;
+ cmd.CommandText = sql;
+ cmd.Parameters.AddWithValue("$project_id", projectId);
+ cmd.ExecuteNonQuery();
+ }
+
+ using var idCmd = conn.CreateCommand();
+ idCmd.Transaction = tx;
+ result = (int?)idCmd.ExecuteScalar() ;
+
+ tx.Commit();
+ }
+ catch
+ {
+ tx.Rollback();
+ throw;
+ }
+
+ return result;
+ }
+
+ }
+}
diff --git a/Forms/MainForm.Designer.cs b/Forms/MainForm.Designer.cs
index 5e6deed..0634acb 100644
--- a/Forms/MainForm.Designer.cs
+++ b/Forms/MainForm.Designer.cs
@@ -34,16 +34,20 @@
MainForm_StatusStrip = new StatusStrip();
tabControlMainForm = new TabControl();
MainForm_TabPage1 = new TabPage();
- button1 = new Button();
MainForm_TabPage2 = new TabPage();
- tableLayoutPanel1Tab2 = new TableLayoutPanel();
+ tableLayoutPanelClients1 = new TableLayoutPanel();
dataGridViewClients = new DataGridView();
+ MainForm_TabPage3 = new TabPage();
+ tableLayoutPanelProjects1 = new TableLayoutPanel();
+ dataGridViewProjects = new DataGridView();
MainForm_MenuStrip.SuspendLayout();
tabControlMainForm.SuspendLayout();
- MainForm_TabPage1.SuspendLayout();
MainForm_TabPage2.SuspendLayout();
- tableLayoutPanel1Tab2.SuspendLayout();
+ tableLayoutPanelClients1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)dataGridViewClients).BeginInit();
+ MainForm_TabPage3.SuspendLayout();
+ tableLayoutPanelProjects1.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)dataGridViewProjects).BeginInit();
SuspendLayout();
//
// MainForm_MenuStrip
@@ -52,7 +56,7 @@
MainForm_MenuStrip.Items.AddRange(new ToolStripItem[] { fileToolStripMenuItem });
MainForm_MenuStrip.Location = new Point(0, 0);
MainForm_MenuStrip.Name = "MainForm_MenuStrip";
- MainForm_MenuStrip.Size = new Size(1343, 40);
+ MainForm_MenuStrip.Size = new Size(1878, 40);
MainForm_MenuStrip.TabIndex = 0;
MainForm_MenuStrip.Text = "menuStrip1";
//
@@ -73,9 +77,9 @@
// MainForm_StatusStrip
//
MainForm_StatusStrip.ImageScalingSize = new Size(32, 32);
- MainForm_StatusStrip.Location = new Point(0, 983);
+ MainForm_StatusStrip.Location = new Point(0, 1060);
MainForm_StatusStrip.Name = "MainForm_StatusStrip";
- MainForm_StatusStrip.Size = new Size(1343, 22);
+ MainForm_StatusStrip.Size = new Size(1878, 22);
MainForm_StatusStrip.TabIndex = 1;
MainForm_StatusStrip.Text = "MainForm_StatusStrip";
//
@@ -83,37 +87,27 @@
//
tabControlMainForm.Controls.Add(MainForm_TabPage1);
tabControlMainForm.Controls.Add(MainForm_TabPage2);
+ tabControlMainForm.Controls.Add(MainForm_TabPage3);
tabControlMainForm.Dock = DockStyle.Fill;
tabControlMainForm.Location = new Point(0, 40);
tabControlMainForm.Name = "tabControlMainForm";
tabControlMainForm.SelectedIndex = 0;
- tabControlMainForm.Size = new Size(1343, 943);
+ tabControlMainForm.Size = new Size(1878, 1020);
tabControlMainForm.TabIndex = 2;
//
// MainForm_TabPage1
//
- MainForm_TabPage1.Controls.Add(button1);
MainForm_TabPage1.Location = new Point(8, 46);
MainForm_TabPage1.Name = "MainForm_TabPage1";
MainForm_TabPage1.Padding = new Padding(3);
- MainForm_TabPage1.Size = new Size(1327, 889);
+ MainForm_TabPage1.Size = new Size(1862, 966);
MainForm_TabPage1.TabIndex = 0;
MainForm_TabPage1.Text = "Tab 1";
MainForm_TabPage1.UseVisualStyleBackColor = true;
//
- // button1
- //
- button1.Location = new Point(39, 47);
- button1.Name = "button1";
- button1.Size = new Size(150, 46);
- button1.TabIndex = 0;
- button1.Text = "button1";
- button1.UseVisualStyleBackColor = true;
- button1.Click += button1_Click;
- //
// MainForm_TabPage2
//
- MainForm_TabPage2.Controls.Add(tableLayoutPanel1Tab2);
+ MainForm_TabPage2.Controls.Add(tableLayoutPanelClients1);
MainForm_TabPage2.Location = new Point(8, 46);
MainForm_TabPage2.Name = "MainForm_TabPage2";
MainForm_TabPage2.Padding = new Padding(3);
@@ -122,20 +116,20 @@
MainForm_TabPage2.Text = "Tab 2";
MainForm_TabPage2.UseVisualStyleBackColor = true;
//
- // tableLayoutPanel1Tab2
+ // tableLayoutPanelClients1
//
- tableLayoutPanel1Tab2.ColumnCount = 1;
- tableLayoutPanel1Tab2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
- tableLayoutPanel1Tab2.Controls.Add(dataGridViewClients, 0, 1);
- tableLayoutPanel1Tab2.Dock = DockStyle.Fill;
- tableLayoutPanel1Tab2.Location = new Point(3, 3);
- tableLayoutPanel1Tab2.Name = "tableLayoutPanel1Tab2";
- tableLayoutPanel1Tab2.RowCount = 3;
- tableLayoutPanel1Tab2.RowStyles.Add(new RowStyle(SizeType.Absolute, 1F));
- tableLayoutPanel1Tab2.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
- tableLayoutPanel1Tab2.RowStyles.Add(new RowStyle(SizeType.Absolute, 200F));
- tableLayoutPanel1Tab2.Size = new Size(1321, 883);
- tableLayoutPanel1Tab2.TabIndex = 0;
+ tableLayoutPanelClients1.ColumnCount = 1;
+ tableLayoutPanelClients1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
+ tableLayoutPanelClients1.Controls.Add(dataGridViewClients, 0, 1);
+ tableLayoutPanelClients1.Dock = DockStyle.Fill;
+ tableLayoutPanelClients1.Location = new Point(3, 3);
+ tableLayoutPanelClients1.Name = "tableLayoutPanelClients1";
+ tableLayoutPanelClients1.RowCount = 3;
+ tableLayoutPanelClients1.RowStyles.Add(new RowStyle(SizeType.Absolute, 1F));
+ tableLayoutPanelClients1.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
+ tableLayoutPanelClients1.RowStyles.Add(new RowStyle(SizeType.Absolute, 200F));
+ tableLayoutPanelClients1.Size = new Size(1321, 883);
+ tableLayoutPanelClients1.TabIndex = 0;
//
// dataGridViewClients
//
@@ -150,11 +144,49 @@
dataGridViewClients.Size = new Size(1315, 676);
dataGridViewClients.TabIndex = 0;
//
+ // MainForm_TabPage3
+ //
+ MainForm_TabPage3.Controls.Add(tableLayoutPanelProjects1);
+ MainForm_TabPage3.Location = new Point(8, 46);
+ MainForm_TabPage3.Name = "MainForm_TabPage3";
+ MainForm_TabPage3.Size = new Size(1327, 889);
+ MainForm_TabPage3.TabIndex = 2;
+ MainForm_TabPage3.Text = "Tab 3";
+ MainForm_TabPage3.UseVisualStyleBackColor = true;
+ //
+ // tableLayoutPanelProjects1
+ //
+ tableLayoutPanelProjects1.ColumnCount = 1;
+ tableLayoutPanelProjects1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
+ tableLayoutPanelProjects1.Controls.Add(dataGridViewProjects, 0, 1);
+ tableLayoutPanelProjects1.Dock = DockStyle.Fill;
+ tableLayoutPanelProjects1.Location = new Point(0, 0);
+ tableLayoutPanelProjects1.Name = "tableLayoutPanelProjects1";
+ tableLayoutPanelProjects1.RowCount = 3;
+ tableLayoutPanelProjects1.RowStyles.Add(new RowStyle(SizeType.Absolute, 1F));
+ tableLayoutPanelProjects1.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
+ tableLayoutPanelProjects1.RowStyles.Add(new RowStyle(SizeType.Absolute, 200F));
+ tableLayoutPanelProjects1.Size = new Size(1327, 889);
+ tableLayoutPanelProjects1.TabIndex = 1;
+ //
+ // dataGridViewProjects
+ //
+ dataGridViewProjects.AllowUserToAddRows = false;
+ dataGridViewProjects.AllowUserToDeleteRows = false;
+ dataGridViewProjects.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+ dataGridViewProjects.Dock = DockStyle.Fill;
+ dataGridViewProjects.Location = new Point(3, 4);
+ dataGridViewProjects.Name = "dataGridViewProjects";
+ dataGridViewProjects.ReadOnly = true;
+ dataGridViewProjects.RowHeadersWidth = 82;
+ dataGridViewProjects.Size = new Size(1321, 682);
+ dataGridViewProjects.TabIndex = 0;
+ //
// MainForm
//
AutoScaleDimensions = new SizeF(13F, 32F);
AutoScaleMode = AutoScaleMode.Font;
- ClientSize = new Size(1343, 1005);
+ ClientSize = new Size(1878, 1082);
Controls.Add(tabControlMainForm);
Controls.Add(MainForm_StatusStrip);
Controls.Add(MainForm_MenuStrip);
@@ -164,10 +196,12 @@
MainForm_MenuStrip.ResumeLayout(false);
MainForm_MenuStrip.PerformLayout();
tabControlMainForm.ResumeLayout(false);
- MainForm_TabPage1.ResumeLayout(false);
MainForm_TabPage2.ResumeLayout(false);
- tableLayoutPanel1Tab2.ResumeLayout(false);
+ tableLayoutPanelClients1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)dataGridViewClients).EndInit();
+ MainForm_TabPage3.ResumeLayout(false);
+ tableLayoutPanelProjects1.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)dataGridViewProjects).EndInit();
ResumeLayout(false);
PerformLayout();
}
@@ -181,8 +215,10 @@
private TabPage MainForm_TabPage2;
private ToolStripMenuItem fileToolStripMenuItem;
private ToolStripMenuItem MainForm_Exit_MenuItem;
- private Button button1;
- private TableLayoutPanel tableLayoutPanel1Tab2;
+ private TableLayoutPanel tableLayoutPanelClients1;
private DataGridView dataGridViewClients;
+ private TabPage MainForm_TabPage3;
+ private TableLayoutPanel tableLayoutPanelProjects1;
+ private DataGridView dataGridViewProjects;
}
}
diff --git a/Forms/MainForm.cs b/Forms/MainForm.cs
index b2f2ba0..7aba030 100644
--- a/Forms/MainForm.cs
+++ b/Forms/MainForm.cs
@@ -36,6 +36,7 @@ namespace trakker
tabControlMainForm.TabPages[0].Text = " Home ";
tabControlMainForm.TabPages[1].Text = " Clients ";
+ tabControlMainForm.TabPages[2].Text = " Projects ";
_ctrl = new Services.MainCtrl(this, connectionString);
}
@@ -58,11 +59,11 @@ namespace trakker
dataGridViewClients.AutoGenerateColumns = false;
dataGridViewClients.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
dataGridViewClients.BackgroundColor = Color.White;
- dataGridViewClients.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
- dataGridViewClients.RowHeadersVisible = false;
dataGridViewClients.ColumnHeadersVisible = true;
- dataGridViewClients.MultiSelect = false;
dataGridViewClients.DataSource = clients;
+ dataGridViewClients.MultiSelect = false;
+ dataGridViewClients.RowHeadersVisible = false;
+ dataGridViewClients.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridViewClients.Columns.Clear();
{
@@ -146,23 +147,160 @@ namespace trakker
};
}
-
- private void button1_Click(object sender, EventArgs e)
+ public void FillDataGridViewProjects(BindingSource projects)
{
- var dialog = new ClientForm(new Client());
- if (dialog.ShowDialog(this) == DialogResult.OK)
- {
- Client client = dialog.Client;
- ClientData clientData = new ClientData(connectionString);
- try
- {
- clientData.Upsert(client);
- }
- catch (Exception ex)
- {
- MessageBox.Show($"Error saving client: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
- }
+ dataGridViewProjects.DataSource = projects;
}
+ public void InitDataGridViewProjects()
+ {
+ 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 = "Status",
+ Name = "Status",
+ Visible = true,
+ };
+ dataGridViewProjects.Columns.Add(textColumn);
+ }
+
+ dataGridViewProjects.DoubleClick += (s, e) =>
+ {
+ if (dataGridViewProjects.SelectedRows.Count > 0)
+ {
+ var selectedProject = dataGridViewProjects.SelectedRows[0].DataBoundItem as Project;
+ if (selectedProject != null)
+ {
+ var dialog = new ProjectForm(selectedProject, _ctrl.GetClients());
+ 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
+ }
+ 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 );
+ }
+ }
+ };
+
+ }
+
+ //private void button1_Click(object sender, EventArgs e)
+ //{
+ // var dialog = new ClientForm(new Client());
+ // if (dialog.ShowDialog(this) == DialogResult.OK)
+ // {
+ // Client client = dialog.Client;
+ // ClientData clientData = new ClientData(connectionString);
+ // try
+ // {
+ // clientData.Upsert(client);
+ // }
+ // catch (Exception ex)
+ // {
+ // MessageBox.Show($"Error saving client: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ // }
+ // }
+ //}
}
}
diff --git a/Forms/ProjectForm.Designer.cs b/Forms/ProjectForm.Designer.cs
new file mode 100644
index 0000000..1adefbd
--- /dev/null
+++ b/Forms/ProjectForm.Designer.cs
@@ -0,0 +1,435 @@
+namespace trakker.Forms
+{
+ partial class ProjectForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ groupBoxNewClient = new GroupBox();
+ tableLayoutPanel1 = new TableLayoutPanel();
+ tableLayoutPanel2 = new TableLayoutPanel();
+ labelClient = new Label();
+ labelName = new Label();
+ labelDescription = new Label();
+ labelStartDate = new Label();
+ labelHourlyRate = new Label();
+ textBoxName = new TextBox();
+ comboBoxClient = new ComboBox();
+ tableLayoutPanel6 = new TableLayoutPanel();
+ dateTimePickerStartDate = new DateTimePicker();
+ dateTimePickerEndDate = new DateTimePicker();
+ labelEndDate = new Label();
+ richTextBoxDescription = new RichTextBox();
+ tableLayoutPanel4 = new TableLayoutPanel();
+ labelBudget = new Label();
+ labelStatus = new Label();
+ comboBoxStatus = new ComboBox();
+ textBoxHourlyRate = new TextBox();
+ textBoxBudget = new TextBox();
+ groupBoxNotes = new GroupBox();
+ richTextBoxNotes = new RichTextBox();
+ tableLayoutPanel3 = new TableLayoutPanel();
+ buttonOkay = new Button();
+ buttonCancel = new Button();
+ labelCreatedUpdatedDT = new Label();
+ groupBoxNewClient.SuspendLayout();
+ tableLayoutPanel1.SuspendLayout();
+ tableLayoutPanel2.SuspendLayout();
+ tableLayoutPanel6.SuspendLayout();
+ tableLayoutPanel4.SuspendLayout();
+ groupBoxNotes.SuspendLayout();
+ tableLayoutPanel3.SuspendLayout();
+ SuspendLayout();
+ //
+ // groupBoxNewClient
+ //
+ groupBoxNewClient.Controls.Add(tableLayoutPanel1);
+ groupBoxNewClient.Dock = DockStyle.Fill;
+ groupBoxNewClient.Location = new Point(0, 0);
+ groupBoxNewClient.Name = "groupBoxNewClient";
+ groupBoxNewClient.Size = new Size(1144, 660);
+ groupBoxNewClient.TabIndex = 1;
+ groupBoxNewClient.TabStop = false;
+ groupBoxNewClient.Text = "New Client";
+ //
+ // tableLayoutPanel1
+ //
+ tableLayoutPanel1.ColumnCount = 1;
+ tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
+ tableLayoutPanel1.Controls.Add(tableLayoutPanel2, 0, 0);
+ tableLayoutPanel1.Controls.Add(groupBoxNotes, 0, 1);
+ tableLayoutPanel1.Controls.Add(tableLayoutPanel3, 0, 2);
+ tableLayoutPanel1.Dock = DockStyle.Fill;
+ tableLayoutPanel1.Location = new Point(3, 35);
+ tableLayoutPanel1.Name = "tableLayoutPanel1";
+ tableLayoutPanel1.RowCount = 3;
+ tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
+ tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Absolute, 242F));
+ tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Absolute, 75F));
+ tableLayoutPanel1.Size = new Size(1138, 622);
+ tableLayoutPanel1.TabIndex = 0;
+ //
+ // tableLayoutPanel2
+ //
+ tableLayoutPanel2.ColumnCount = 2;
+ tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 150F));
+ tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
+ tableLayoutPanel2.Controls.Add(labelClient, 0, 0);
+ tableLayoutPanel2.Controls.Add(labelName, 0, 1);
+ tableLayoutPanel2.Controls.Add(labelDescription, 0, 2);
+ tableLayoutPanel2.Controls.Add(labelStartDate, 0, 3);
+ tableLayoutPanel2.Controls.Add(labelHourlyRate, 0, 4);
+ tableLayoutPanel2.Controls.Add(textBoxName, 1, 1);
+ tableLayoutPanel2.Controls.Add(comboBoxClient, 1, 0);
+ tableLayoutPanel2.Controls.Add(tableLayoutPanel6, 1, 3);
+ tableLayoutPanel2.Controls.Add(richTextBoxDescription, 1, 2);
+ tableLayoutPanel2.Controls.Add(tableLayoutPanel4, 1, 4);
+ tableLayoutPanel2.Dock = DockStyle.Fill;
+ tableLayoutPanel2.Location = new Point(3, 3);
+ tableLayoutPanel2.Name = "tableLayoutPanel2";
+ tableLayoutPanel2.RowCount = 5;
+ tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Absolute, 50F));
+ tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Absolute, 50F));
+ tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Absolute, 100F));
+ tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Absolute, 50F));
+ tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Absolute, 50F));
+ tableLayoutPanel2.Size = new Size(1132, 299);
+ tableLayoutPanel2.TabIndex = 1;
+ //
+ // labelClient
+ //
+ labelClient.AutoSize = true;
+ labelClient.Dock = DockStyle.Fill;
+ labelClient.Location = new Point(3, 0);
+ labelClient.Name = "labelClient";
+ labelClient.Size = new Size(144, 50);
+ labelClient.TabIndex = 0;
+ labelClient.Text = "Client *";
+ //
+ // labelName
+ //
+ labelName.AutoSize = true;
+ labelName.Dock = DockStyle.Fill;
+ labelName.Location = new Point(3, 50);
+ labelName.Name = "labelName";
+ labelName.Size = new Size(144, 50);
+ labelName.TabIndex = 0;
+ labelName.Text = "Name *";
+ //
+ // labelDescription
+ //
+ labelDescription.AutoSize = true;
+ labelDescription.Dock = DockStyle.Fill;
+ labelDescription.Location = new Point(3, 100);
+ labelDescription.Name = "labelDescription";
+ labelDescription.Size = new Size(144, 100);
+ labelDescription.TabIndex = 0;
+ labelDescription.Text = "Description";
+ //
+ // labelStartDate
+ //
+ labelStartDate.AutoSize = true;
+ labelStartDate.Dock = DockStyle.Fill;
+ labelStartDate.Location = new Point(3, 200);
+ labelStartDate.Name = "labelStartDate";
+ labelStartDate.Size = new Size(144, 50);
+ labelStartDate.TabIndex = 0;
+ labelStartDate.Text = "Start Date";
+ //
+ // labelHourlyRate
+ //
+ labelHourlyRate.AutoSize = true;
+ labelHourlyRate.Dock = DockStyle.Fill;
+ labelHourlyRate.Location = new Point(3, 250);
+ labelHourlyRate.Name = "labelHourlyRate";
+ labelHourlyRate.Size = new Size(144, 50);
+ labelHourlyRate.TabIndex = 0;
+ labelHourlyRate.Text = "Hourly Rate";
+ //
+ // textBoxName
+ //
+ textBoxName.Dock = DockStyle.Left;
+ textBoxName.Location = new Point(153, 53);
+ textBoxName.Name = "textBoxName";
+ textBoxName.PlaceholderText = "Project X";
+ textBoxName.Size = new Size(913, 39);
+ textBoxName.TabIndex = 2;
+ textBoxName.Validating += textBoxName_Validating;
+ //
+ // comboBoxClient
+ //
+ comboBoxClient.Dock = DockStyle.Fill;
+ comboBoxClient.FlatStyle = FlatStyle.Flat;
+ comboBoxClient.FormattingEnabled = true;
+ comboBoxClient.Location = new Point(153, 3);
+ comboBoxClient.Name = "comboBoxClient";
+ comboBoxClient.Size = new Size(976, 40);
+ comboBoxClient.TabIndex = 1;
+ //
+ // tableLayoutPanel6
+ //
+ tableLayoutPanel6.ColumnCount = 3;
+ tableLayoutPanel6.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 40F));
+ tableLayoutPanel6.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20F));
+ tableLayoutPanel6.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 40F));
+ tableLayoutPanel6.Controls.Add(dateTimePickerStartDate, 0, 0);
+ tableLayoutPanel6.Controls.Add(dateTimePickerEndDate, 2, 0);
+ tableLayoutPanel6.Controls.Add(labelEndDate, 1, 0);
+ tableLayoutPanel6.Dock = DockStyle.Fill;
+ tableLayoutPanel6.Location = new Point(153, 203);
+ tableLayoutPanel6.Name = "tableLayoutPanel6";
+ tableLayoutPanel6.RowCount = 1;
+ tableLayoutPanel6.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
+ tableLayoutPanel6.Size = new Size(976, 44);
+ tableLayoutPanel6.TabIndex = 7;
+ //
+ // dateTimePickerStartDate
+ //
+ dateTimePickerStartDate.Format = DateTimePickerFormat.Short;
+ dateTimePickerStartDate.Location = new Point(3, 3);
+ dateTimePickerStartDate.Name = "dateTimePickerStartDate";
+ dateTimePickerStartDate.Size = new Size(384, 39);
+ dateTimePickerStartDate.TabIndex = 0;
+ dateTimePickerStartDate.TabStop = false;
+ //
+ // dateTimePickerEndDate
+ //
+ dateTimePickerEndDate.Format = DateTimePickerFormat.Short;
+ dateTimePickerEndDate.Location = new Point(588, 3);
+ dateTimePickerEndDate.Name = "dateTimePickerEndDate";
+ dateTimePickerEndDate.Size = new Size(385, 39);
+ dateTimePickerEndDate.TabIndex = 1;
+ dateTimePickerEndDate.TabStop = false;
+ //
+ // labelEndDate
+ //
+ labelEndDate.AutoSize = true;
+ labelEndDate.Dock = DockStyle.Fill;
+ labelEndDate.Location = new Point(393, 0);
+ labelEndDate.Name = "labelEndDate";
+ labelEndDate.Size = new Size(189, 44);
+ labelEndDate.TabIndex = 2;
+ labelEndDate.Text = "End Date";
+ labelEndDate.TextAlign = ContentAlignment.TopCenter;
+ //
+ // richTextBoxDescription
+ //
+ richTextBoxDescription.Dock = DockStyle.Fill;
+ richTextBoxDescription.Location = new Point(153, 103);
+ richTextBoxDescription.Name = "richTextBoxDescription";
+ richTextBoxDescription.Size = new Size(976, 94);
+ richTextBoxDescription.TabIndex = 3;
+ richTextBoxDescription.Text = "";
+ //
+ // tableLayoutPanel4
+ //
+ tableLayoutPanel4.ColumnCount = 5;
+ tableLayoutPanel4.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20F));
+ tableLayoutPanel4.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20F));
+ tableLayoutPanel4.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20F));
+ tableLayoutPanel4.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20F));
+ tableLayoutPanel4.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20F));
+ tableLayoutPanel4.Controls.Add(labelBudget, 1, 0);
+ tableLayoutPanel4.Controls.Add(labelStatus, 3, 0);
+ tableLayoutPanel4.Controls.Add(comboBoxStatus, 4, 0);
+ tableLayoutPanel4.Controls.Add(textBoxHourlyRate, 0, 0);
+ tableLayoutPanel4.Controls.Add(textBoxBudget, 2, 0);
+ tableLayoutPanel4.Dock = DockStyle.Fill;
+ tableLayoutPanel4.Location = new Point(153, 253);
+ tableLayoutPanel4.Name = "tableLayoutPanel4";
+ tableLayoutPanel4.RowCount = 1;
+ tableLayoutPanel4.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
+ tableLayoutPanel4.Size = new Size(976, 44);
+ tableLayoutPanel4.TabIndex = 9;
+ //
+ // labelBudget
+ //
+ labelBudget.AutoSize = true;
+ labelBudget.Dock = DockStyle.Fill;
+ labelBudget.Location = new Point(198, 0);
+ labelBudget.Name = "labelBudget";
+ labelBudget.Size = new Size(189, 44);
+ labelBudget.TabIndex = 0;
+ labelBudget.Text = "Budget";
+ labelBudget.TextAlign = ContentAlignment.TopCenter;
+ //
+ // labelStatus
+ //
+ labelStatus.AutoSize = true;
+ labelStatus.Dock = DockStyle.Fill;
+ labelStatus.Location = new Point(588, 0);
+ labelStatus.Name = "labelStatus";
+ labelStatus.Size = new Size(189, 44);
+ labelStatus.TabIndex = 1;
+ labelStatus.Text = "Status";
+ labelStatus.TextAlign = ContentAlignment.TopCenter;
+ //
+ // comboBoxStatus
+ //
+ comboBoxStatus.FlatStyle = FlatStyle.Flat;
+ comboBoxStatus.FormattingEnabled = true;
+ comboBoxStatus.Location = new Point(783, 3);
+ comboBoxStatus.Name = "comboBoxStatus";
+ comboBoxStatus.Size = new Size(190, 40);
+ comboBoxStatus.TabIndex = 6;
+ //
+ // textBoxHourlyRate
+ //
+ textBoxHourlyRate.Location = new Point(3, 3);
+ textBoxHourlyRate.Name = "textBoxHourlyRate";
+ textBoxHourlyRate.Size = new Size(189, 39);
+ textBoxHourlyRate.TabIndex = 7;
+ //
+ // textBoxBudget
+ //
+ textBoxBudget.Location = new Point(393, 3);
+ textBoxBudget.Name = "textBoxBudget";
+ textBoxBudget.Size = new Size(189, 39);
+ textBoxBudget.TabIndex = 8;
+ //
+ // groupBoxNotes
+ //
+ groupBoxNotes.Controls.Add(richTextBoxNotes);
+ groupBoxNotes.Dock = DockStyle.Fill;
+ groupBoxNotes.Location = new Point(3, 308);
+ groupBoxNotes.Name = "groupBoxNotes";
+ groupBoxNotes.Size = new Size(1132, 236);
+ groupBoxNotes.TabIndex = 0;
+ groupBoxNotes.TabStop = false;
+ groupBoxNotes.Text = "Notes";
+ //
+ // richTextBoxNotes
+ //
+ richTextBoxNotes.Dock = DockStyle.Fill;
+ richTextBoxNotes.Location = new Point(3, 35);
+ richTextBoxNotes.Name = "richTextBoxNotes";
+ richTextBoxNotes.Size = new Size(1126, 198);
+ richTextBoxNotes.TabIndex = 7;
+ richTextBoxNotes.Text = "";
+ //
+ // tableLayoutPanel3
+ //
+ tableLayoutPanel3.ColumnCount = 3;
+ tableLayoutPanel3.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
+ tableLayoutPanel3.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 200F));
+ tableLayoutPanel3.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 200F));
+ tableLayoutPanel3.Controls.Add(buttonOkay, 1, 0);
+ tableLayoutPanel3.Controls.Add(buttonCancel, 2, 0);
+ tableLayoutPanel3.Controls.Add(labelCreatedUpdatedDT, 0, 0);
+ tableLayoutPanel3.Dock = DockStyle.Fill;
+ tableLayoutPanel3.Location = new Point(3, 550);
+ tableLayoutPanel3.Name = "tableLayoutPanel3";
+ tableLayoutPanel3.RowCount = 1;
+ tableLayoutPanel3.RowStyles.Add(new RowStyle(SizeType.Absolute, 75F));
+ tableLayoutPanel3.Size = new Size(1132, 69);
+ tableLayoutPanel3.TabIndex = 2;
+ //
+ // buttonOkay
+ //
+ buttonOkay.Dock = DockStyle.Fill;
+ buttonOkay.Location = new Point(735, 3);
+ buttonOkay.Margin = new Padding(3, 3, 3, 15);
+ buttonOkay.Name = "buttonOkay";
+ buttonOkay.Size = new Size(194, 57);
+ buttonOkay.TabIndex = 8;
+ buttonOkay.Text = "Okay";
+ buttonOkay.UseVisualStyleBackColor = true;
+ //
+ // buttonCancel
+ //
+ buttonCancel.Dock = DockStyle.Fill;
+ buttonCancel.Location = new Point(935, 3);
+ buttonCancel.Margin = new Padding(3, 3, 3, 15);
+ buttonCancel.Name = "buttonCancel";
+ buttonCancel.Size = new Size(194, 57);
+ buttonCancel.TabIndex = 99;
+ buttonCancel.TabStop = false;
+ buttonCancel.Text = "Cancel";
+ buttonCancel.UseVisualStyleBackColor = true;
+ //
+ // labelCreatedUpdatedDT
+ //
+ labelCreatedUpdatedDT.AutoSize = true;
+ labelCreatedUpdatedDT.Dock = DockStyle.Fill;
+ labelCreatedUpdatedDT.Location = new Point(3, 0);
+ labelCreatedUpdatedDT.Name = "labelCreatedUpdatedDT";
+ labelCreatedUpdatedDT.Size = new Size(726, 75);
+ labelCreatedUpdatedDT.TabIndex = 2;
+ labelCreatedUpdatedDT.Text = "Created: yyyy-mm-dd, Updated: yyyy-mm-dd";
+ //
+ // ProjectForm
+ //
+ AutoScaleDimensions = new SizeF(13F, 32F);
+ AutoScaleMode = AutoScaleMode.Font;
+ ClientSize = new Size(1144, 660);
+ Controls.Add(groupBoxNewClient);
+ Name = "ProjectForm";
+ Text = "ProjectForm";
+ groupBoxNewClient.ResumeLayout(false);
+ tableLayoutPanel1.ResumeLayout(false);
+ tableLayoutPanel2.ResumeLayout(false);
+ tableLayoutPanel2.PerformLayout();
+ tableLayoutPanel6.ResumeLayout(false);
+ tableLayoutPanel6.PerformLayout();
+ tableLayoutPanel4.ResumeLayout(false);
+ tableLayoutPanel4.PerformLayout();
+ groupBoxNotes.ResumeLayout(false);
+ tableLayoutPanel3.ResumeLayout(false);
+ tableLayoutPanel3.PerformLayout();
+ ResumeLayout(false);
+ }
+
+ #endregion
+
+ private GroupBox groupBoxNewClient;
+ private TableLayoutPanel tableLayoutPanel1;
+ private TableLayoutPanel tableLayoutPanel2;
+ private Label labelClient;
+ private Label labelName;
+ private Label labelDescription;
+ private Label labelStartDate;
+ private Label labelHourlyRate;
+ private TextBox textBoxName;
+ private GroupBox groupBoxNotes;
+ private RichTextBox richTextBoxNotes;
+ private TableLayoutPanel tableLayoutPanel3;
+ private Button buttonOkay;
+ private Button buttonCancel;
+ private Label labelCreatedUpdatedDT;
+ private ComboBox comboBoxClient;
+ private TableLayoutPanel tableLayoutPanel6;
+ private DateTimePicker dateTimePickerStartDate;
+ private DateTimePicker dateTimePickerEndDate;
+ private Label labelEndDate;
+ private RichTextBox richTextBoxDescription;
+ private TableLayoutPanel tableLayoutPanel4;
+ private Label labelBudget;
+ private Label labelStatus;
+ private ComboBox comboBoxStatus;
+ private TextBox textBoxHourlyRate;
+ private TextBox textBoxBudget;
+ }
+}
\ No newline at end of file
diff --git a/Forms/ProjectForm.cs b/Forms/ProjectForm.cs
new file mode 100644
index 0000000..cb462c8
--- /dev/null
+++ b/Forms/ProjectForm.cs
@@ -0,0 +1,120 @@
+using System.ComponentModel;
+using trakker.Models;
+
+namespace trakker.Forms
+{
+ ///
+ /// Form used to view and edit a model. Fields on the form
+ /// are data-bound to the provided project instance and basic validation is
+ /// performed on required fields.
+ ///
+ public partial class ProjectForm : Form
+ {
+ ///
+ /// The project instance being edited by this form.
+ ///
+ private readonly Project _project;
+
+ private BindingList? _clients;
+
+ ///
+ /// Binding source that connects the project model to the form controls.
+ ///
+ private BindingSource bindingSource = new BindingSource();
+
+ ///
+ /// Error provider used to display validation errors next to input controls.
+ ///
+ private ErrorProvider errorProvider = new ErrorProvider();
+
+ ///
+ /// Initializes a new instance of the class bound to
+ /// the provided . Sets up data bindings for all
+ /// visible input controls and configures dialog button behavior.
+ ///
+ /// The instance to edit. Must not be null.
+ /// The list of instances to select from. Must not be null.
+ public ProjectForm(Project project, BindingList clients)
+ {
+ _project = project;
+ _clients = clients;
+ InitializeComponent();
+
+ comboBoxClient.DataSource = _clients;
+ comboBoxClient.DisplayMember = "Name";
+ comboBoxClient.ValueMember = "ClientId";
+
+ dateTimePickerStartDate.Format = DateTimePickerFormat.Custom;
+ dateTimePickerStartDate.CustomFormat = "yyyy-MM-dd";
+ dateTimePickerEndDate.Format = DateTimePickerFormat.Custom;
+ dateTimePickerEndDate.CustomFormat = "yyyy-MM-dd";
+
+ // Bind model properties to controls so the UI reflects and updates the model.
+ bindingSource.DataSource = _project;
+ comboBoxClient.DataBindings.Add("SelectedValue", bindingSource, "ClientId", true);
+ textBoxName.DataBindings.Add("Text", bindingSource, "ProjectName", true);
+ richTextBoxDescription.DataBindings.Add("Text", bindingSource, "Description", true);
+ dateTimePickerStartDate.DataBindings.Add("Value", bindingSource, "StartDate", true);
+ dateTimePickerEndDate.DataBindings.Add("Value", bindingSource, "EndDate", true);
+ textBoxHourlyRate.DataBindings.Add("Text", bindingSource, "HourlyRate", true);
+ textBoxBudget.DataBindings.Add("Text", bindingSource, "Budget", true);
+ comboBoxStatus.DataBindings.Add("ValueMember", bindingSource, "Status", true);
+ richTextBoxNotes.DataBindings.Add("Text", bindingSource, "Notes", true);
+
+ // Configure dialog buttons and window behavior.
+ buttonOkay.DialogResult = DialogResult.OK;
+ buttonCancel.DialogResult = DialogResult.Cancel;
+ this.CancelButton = buttonCancel;
+ this.StartPosition = FormStartPosition.CenterParent;
+ }
+
+ ///
+ /// Gets the instance edited by the form.
+ ///
+ public Project Project { get => _project; private set { } }
+
+ public BindingList? Clients { get => _clients; set { _clients = value; } }
+
+ ///
+ /// Validates the Name field. If the name is empty or whitespace, an error is set
+ /// on the and the event is canceled to prevent the
+ /// form from closing.
+ ///
+ private void textBoxName_Validating(object sender, System.ComponentModel.CancelEventArgs e)
+ {
+ if (string.IsNullOrWhiteSpace(textBoxName.Text))
+ {
+ errorProvider.SetError(textBoxName, "Name is required.");
+ errorProvider.SetIconAlignment(textBoxName, ErrorIconAlignment.MiddleRight);
+ errorProvider.SetIconPadding(textBoxName, 2);
+ e.Cancel = true;
+ }
+ else
+ {
+ errorProvider.SetError(textBoxName, "");
+ }
+ }
+
+ ///
+ /// Validates the Email field. If the email is empty or whitespace, an error is set
+ /// on the and the event is canceled to prevent the
+ /// form from closing. Note: this validation only checks presence, not format.
+ ///
+ private void textBoxEmail_Validating(object sender, System.ComponentModel.CancelEventArgs e)
+ {
+ //if (string.IsNullOrWhiteSpace(textBoxEmail.Text))
+ //{
+ // errorProvider.SetError(textBoxEmail, "Email is required.");
+ // errorProvider.SetIconAlignment(textBoxEmail, ErrorIconAlignment.MiddleRight);
+ // errorProvider.SetIconPadding(textBoxEmail, 2);
+ // e.Cancel = true;
+ //}
+ //else
+ //{
+ // errorProvider.SetError(textBoxEmail, "");
+ //}
+ }
+
+ }
+}
+
diff --git a/Forms/ProjectForm.resx b/Forms/ProjectForm.resx
new file mode 100644
index 0000000..8b2ff64
--- /dev/null
+++ b/Forms/ProjectForm.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Interfaces/IMainForm.cs b/Interfaces/IMainForm.cs
index 4f5c57c..d537a2d 100644
--- a/Interfaces/IMainForm.cs
+++ b/Interfaces/IMainForm.cs
@@ -11,5 +11,7 @@ namespace trakker.Interfaces
internal interface IMainForm
{
void InitDataGridViewClients(BindingList clients);
+ void InitDataGridViewProjects();
+ void FillDataGridViewProjects(BindingSource projects);
}
}
diff --git a/Models/Project.cs b/Models/Project.cs
index 4a308cf..ed26ea1 100644
--- a/Models/Project.cs
+++ b/Models/Project.cs
@@ -1,37 +1,31 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-
+
namespace trakker.Models
{
public class Project
{
- [Key]
public string ProjectId { get; set; } = string.Empty;
- [Required]
public string ClientId { get; set; } = string.Empty;
- [Required]
- [MaxLength(200)]
- public string Name { get; set; } = string.Empty;
+ public string ProjectCode { get; set; } = string.Empty;
- [MaxLength(1000)]
- public string? Description { get; set; }
+ public string ClientName { get; set; } = string.Empty;
- public DateOnly? StartDate { get; set; }
+ public string ProjectName { get; set; } = string.Empty;
- public DateOnly? EndDate { get; set; }
+ public string? Description { get; set; } = string.Empty;
+
+ public DateTime? StartDate { get; set; }
+
+ public DateTime? EndDate { get; set; }
- [Range(0, double.MaxValue)]
public decimal Budget { get; set; } = 0;
- public ProjectStatus Status { get; set; } = ProjectStatus.Active;
+ public string Status { get; set; } = string.Empty;
- [Range(0, double.MaxValue)]
public decimal? HourlyRate { get; set; }
- [MaxLength(2000)]
- public string? Notes { get; set; }
+ public string? Notes { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
diff --git a/Services/MainCtrl.cs b/Services/MainCtrl.cs
index 01a672a..187aa79 100644
--- a/Services/MainCtrl.cs
+++ b/Services/MainCtrl.cs
@@ -20,13 +20,36 @@ namespace trakker.Services
_connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString));
LoadClients();
+
+ _view.InitDataGridViewProjects();
+ LoadProjects();
+ }
+
+ public BindingList GetClients()
+ {
+ var dbo = new Data.ClientData(_connectionString);
+ return dbo.Get();
}
internal void LoadClients()
{
- // Implement logic to load clients from the database using _connectionString
- var dbo = new Data.ClientData(_connectionString);
- var clients = dbo.Get();
+ var clients = GetClients();
_view.InitDataGridViewClients(clients);
}
+
+ public BindingList GetProjects()
+ {
+ var dbo = new Data.ProjectData(_connectionString);
+ return dbo.Get();
+ }
+
+ internal void LoadProjects()
+ {
+ var projects = GetProjects();
+ foreach (var project in projects)
+ {
+ var x = project.Status;
+ }
+ _view.FillDataGridViewProjects(new BindingSource { DataSource = projects });
+ }
}
}
diff --git a/backup.txt b/backup.txt
new file mode 100644
index 0000000..9923c00
--- /dev/null
+++ b/backup.txt
@@ -0,0 +1,285 @@
+using Microsoft.Data.Sqlite;
+using System.ComponentModel;
+using trakker.Data;
+using trakker.Forms;
+using trakker.Interfaces;
+using trakker.Models;
+using trakker.Services;
+
+namespace trakker
+{
+ public partial class MainForm : Form, IMainForm
+ {
+ //private readonly string _dbversion = "[N.N.N]";
+ private string connectionString = string.Empty;
+ readonly MainCtrl _ctrl;
+
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Sets up the form's controls and event handlers by calling
+ /// which is generated by the designer.
+ ///
+ 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();
+
+ tabControlMainForm.TabPages[0].Text = " Home ";
+ tabControlMainForm.TabPages[1].Text = " Clients ";
+ tabControlMainForm.TabPages[2].Text = " Projects ";
+
+ _ctrl = new Services.MainCtrl(this, connectionString);
+ }
+
+ ///
+ /// Handles the Click event of the Exit menu item. When invoked, this
+ /// method terminates the application.
+ ///
+ /// The source of the event.
+ /// Event data associated with the click event.
+ private void MainForm_Exit_MenuItem_Click(object sender, EventArgs e)
+ {
+ Application.Exit();
+ }
+
+ public void InitDataGridViewClients(BindingList clients)
+ {
+ dataGridViewClients.AllowUserToAddRows = true;
+ dataGridViewClients.AllowUserToDeleteRows = true;
+ dataGridViewClients.AutoGenerateColumns = false;
+ dataGridViewClients.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
+ dataGridViewClients.BackgroundColor = Color.White;
+ dataGridViewClients.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
+ dataGridViewClients.RowHeadersVisible = false;
+ dataGridViewClients.ColumnHeadersVisible = true;
+ dataGridViewClients.MultiSelect = false;
+ dataGridViewClients.DataSource = clients;
+
+ 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) =>
+ {
+ if (dataGridViewClients.SelectedRows.Count > 0)
+ {
+ var selectedClient = dataGridViewClients.SelectedRows[0].DataBoundItem as Client;
+ if (selectedClient != null)
+ {
+ var dialog = new ClientForm(selectedClient);
+ 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);
+ }
+ }
+ }
+ }
+ };
+
+ 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 InitDataGridViewProjects(BindingList projects)
+ {
+ dataGridViewProjects.AllowUserToAddRows = true;
+ dataGridViewProjects.AllowUserToDeleteRows = true;
+ dataGridViewProjects.AutoGenerateColumns = false;
+ dataGridViewProjects.BackgroundColor = Color.White;
+ dataGridViewProjects.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
+ dataGridViewProjects.RowHeadersVisible = false;
+ dataGridViewProjects.ColumnHeadersVisible = true;
+ dataGridViewProjects.MultiSelect = false;
+ dataGridViewProjects.DataSource = projects;
+ dataGridViewProjects.Columns.Clear();
+ {
+ var textColumn = new DataGridViewTextBoxColumn
+ {
+ AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
+ DataPropertyName = "ProjectName",
+ Name = "Project Name",
+ Visible = true,
+ };
+ 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",
+ Name = "Start Date",
+ Visible = true,
+ };
+ dataGridViewProjects.Columns.Add(textColumn);
+ }
+ {
+ var textColumn = new DataGridViewTextBoxColumn
+ {
+ AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
+ DataPropertyName = "EndDate",
+ Name = "End Date",
+ Visible = true,
+ };
+ dataGridViewProjects.Columns.Add(textColumn);
+ }
+ {
+ var textColumn = new DataGridViewTextBoxColumn
+ {
+ AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
+ DataPropertyName = "Status",
+ Name = "Status",
+ Visible = true,
+ };
+ 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);
+ }
+
+ dataGridViewProjects.DoubleClick += (s, e) =>
+ {
+ if (dataGridViewProjects.SelectedRows.Count > 0)
+ {
+ var selectedProject = dataGridViewProjects.SelectedRows[0].DataBoundItem as Project;
+ if (selectedProject != null)
+ {
+ var dialog = new ProjectForm(selectedProject);
+ if (dialog.ShowDialog(this) == DialogResult.OK)
+ {
+ // Project project = dialog.Project;
+ // ProjectData projectData = new ProjectData(connectionString);
+ // try
+ // {
+ // projectData.Upsert(project);
+ // dataGridViewProjects.Refresh(); // Refresh the DataGridView to reflect changes
+ // }
+ // catch (Exception ex)
+ // {
+ // MessageBox.Show($"Error saving project: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ // }
+ }
+ }
+ }
+ };
+
+ 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 );
+ }
+ }
+ };
+
+ }
+
+ //private void button1_Click(object sender, EventArgs e)
+ //{
+ // var dialog = new ClientForm(new Client());
+ // if (dialog.ShowDialog(this) == DialogResult.OK)
+ // {
+ // Client client = dialog.Client;
+ // ClientData clientData = new ClientData(connectionString);
+ // try
+ // {
+ // clientData.Upsert(client);
+ // }
+ // catch (Exception ex)
+ // {
+ // MessageBox.Show($"Error saving client: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ // }
+ // }
+ //}
+ }
+}