Continued development

This commit is contained in:
c0d3.m0nk3y 2026-05-08 22:12:31 -04:00
parent 691a999bae
commit 1b4aa175ba
11 changed files with 165 additions and 29 deletions

49
Data/LOVData.cs Normal file
View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using trakker.Models;
namespace trakker.Data
{
internal class LOVData(string connectionString) : DataAccess(connectionString)
{
public BindingList<LOV> Get(string source)
{
var results = new BindingList<LOV>();
string sql = $@"
SELECT
value,
display
FROM
lov
WHERE
source = $source
ORDER BY
sort ASC, display ASC
;
";
using var conn = OpenConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = sql;
cmd.Parameters.AddWithValue("$source", source);
using var reader = cmd.ExecuteReader();
var _var1 = reader.GetOrdinal("value");
var _var2 = reader.GetOrdinal("display");
while (reader.Read())
{
results.Add(new LOV(reader.GetString(_var1), reader.GetString(_var2)));
}
return results;
}
}
}

View File

@ -33,14 +33,16 @@ namespace trakker.Data
p.end_date,
p.budget,
p.status,
l.display AS status_name,
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}
JOIN (SELECT value, display FROM lov WHERE source = 'project.status') l ON p.status = l.value
WHERE
{whereClause}
ORDER BY p.start_date DESC, p.name ASC;
;
";
@ -65,11 +67,11 @@ namespace trakker.Data
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");
var _var11 = reader.GetOrdinal("status_name");
var _var12 = reader.GetOrdinal("hourly_rate");
var _var13 = reader.GetOrdinal("notes");
var _var14 = reader.GetOrdinal("created_at");
var _var15 = reader.GetOrdinal("updated_at");
while (reader.Read())
{
results.Add(new Project
@ -84,10 +86,11 @@ namespace trakker.Data
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),
StatusName = reader.GetString(_var11),
HourlyRate = reader.GetDecimal(_var12),
Notes = reader.GetString(_var13),
CreatedAt = reader.GetDateTime(_var14),
UpdatedAt = reader.GetDateTime(_var15),
});
}

View File

@ -70,7 +70,7 @@
groupBoxNewClient.Size = new Size(1128, 665);
groupBoxNewClient.TabIndex = 0;
groupBoxNewClient.TabStop = false;
groupBoxNewClient.Text = "New Client";
groupBoxNewClient.Text = "Client";
//
// tableLayoutPanel1
//

View File

@ -30,11 +30,14 @@ namespace trakker.Forms
/// visible input controls and configures dialog button behavior.
/// </summary>
/// <param name="client">The <see cref="Client"/> instance to edit. Must not be null.</param>
public ClientForm(Client client)
/// <param name="states">The binding source for state values. Must not be null.</param>
public ClientForm(Client client, BindingSource states)
{
_client = client;
InitializeComponent();
Text = "Client Details";
// Bind model properties to controls so the UI reflects and updates the model.
bindingSource.DataSource = _client;
textBoxName.DataBindings.Add("Text", bindingSource, "Name", true);
@ -43,7 +46,10 @@ namespace trakker.Forms
maskedTextBox_Phone.DataBindings.Add("Text", bindingSource, "Phone", true);
textBoxAddressStreet.DataBindings.Add("Text", bindingSource, "AddressStreet", true);
textBoxAddressCity.DataBindings.Add("Text", bindingSource, "AddressCity", true);
comboBoxAddressState.DataBindings.Add("Text", bindingSource, "AddressState", true);
comboBoxAddressState.DataSource = states;
comboBoxAddressState.DisplayMember = "Display";
comboBoxAddressState.ValueMember = "Value";
comboBoxAddressState.DataBindings.Add("SelectedValue", bindingSource, "AddressState", true);
maskedTextBoxAddressPostal.DataBindings.Add("Text", bindingSource, "AddressPostal", true);
richTextBoxNotes.DataBindings.Add("Text", bindingSource, "Notes", true);

View File

@ -40,6 +40,9 @@
MainForm_TabPage3 = new TabPage();
tableLayoutPanelProjects1 = new TableLayoutPanel();
dataGridViewProjects = new DataGridView();
tableLayoutPanelProjects2 = new TableLayoutPanel();
groupBoxProjectTasks = new GroupBox();
dataGridViewProjectTasks = new DataGridView();
MainForm_MenuStrip.SuspendLayout();
tabControlMainForm.SuspendLayout();
MainForm_TabPage2.SuspendLayout();
@ -48,6 +51,9 @@
MainForm_TabPage3.SuspendLayout();
tableLayoutPanelProjects1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)dataGridViewProjects).BeginInit();
tableLayoutPanelProjects2.SuspendLayout();
groupBoxProjectTasks.SuspendLayout();
((System.ComponentModel.ISupportInitialize)dataGridViewProjectTasks).BeginInit();
SuspendLayout();
//
// MainForm_MenuStrip
@ -111,7 +117,7 @@
MainForm_TabPage2.Location = new Point(8, 46);
MainForm_TabPage2.Name = "MainForm_TabPage2";
MainForm_TabPage2.Padding = new Padding(3);
MainForm_TabPage2.Size = new Size(1327, 889);
MainForm_TabPage2.Size = new Size(1862, 966);
MainForm_TabPage2.TabIndex = 1;
MainForm_TabPage2.Text = "Tab 2";
MainForm_TabPage2.UseVisualStyleBackColor = true;
@ -127,8 +133,8 @@
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.RowStyles.Add(new RowStyle(SizeType.Percent, 0F));
tableLayoutPanelClients1.Size = new Size(1856, 960);
tableLayoutPanelClients1.TabIndex = 0;
//
// dataGridViewClients
@ -141,7 +147,7 @@
dataGridViewClients.Name = "dataGridViewClients";
dataGridViewClients.ReadOnly = true;
dataGridViewClients.RowHeadersWidth = 82;
dataGridViewClients.Size = new Size(1315, 676);
dataGridViewClients.Size = new Size(1850, 953);
dataGridViewClients.TabIndex = 0;
//
// MainForm_TabPage3
@ -149,7 +155,7 @@
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.Size = new Size(1862, 966);
MainForm_TabPage3.TabIndex = 2;
MainForm_TabPage3.Text = "Tab 3";
MainForm_TabPage3.UseVisualStyleBackColor = true;
@ -159,14 +165,15 @@
tableLayoutPanelProjects1.ColumnCount = 1;
tableLayoutPanelProjects1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
tableLayoutPanelProjects1.Controls.Add(dataGridViewProjects, 0, 1);
tableLayoutPanelProjects1.Controls.Add(tableLayoutPanelProjects2, 0, 2);
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.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
tableLayoutPanelProjects1.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
tableLayoutPanelProjects1.Size = new Size(1862, 966);
tableLayoutPanelProjects1.TabIndex = 1;
//
// dataGridViewProjects
@ -179,9 +186,48 @@
dataGridViewProjects.Name = "dataGridViewProjects";
dataGridViewProjects.ReadOnly = true;
dataGridViewProjects.RowHeadersWidth = 82;
dataGridViewProjects.Size = new Size(1321, 682);
dataGridViewProjects.Size = new Size(1856, 476);
dataGridViewProjects.TabIndex = 0;
//
// tableLayoutPanelProjects2
//
tableLayoutPanelProjects2.ColumnCount = 3;
tableLayoutPanelProjects2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 0F));
tableLayoutPanelProjects2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
tableLayoutPanelProjects2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 0F));
tableLayoutPanelProjects2.Controls.Add(groupBoxProjectTasks, 1, 0);
tableLayoutPanelProjects2.Dock = DockStyle.Fill;
tableLayoutPanelProjects2.Location = new Point(3, 486);
tableLayoutPanelProjects2.Name = "tableLayoutPanelProjects2";
tableLayoutPanelProjects2.RowCount = 1;
tableLayoutPanelProjects2.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
tableLayoutPanelProjects2.Size = new Size(1856, 477);
tableLayoutPanelProjects2.TabIndex = 1;
//
// groupBoxProjectTasks
//
groupBoxProjectTasks.Controls.Add(dataGridViewProjectTasks);
groupBoxProjectTasks.Dock = DockStyle.Fill;
groupBoxProjectTasks.Location = new Point(3, 3);
groupBoxProjectTasks.Name = "groupBoxProjectTasks";
groupBoxProjectTasks.Size = new Size(1850, 471);
groupBoxProjectTasks.TabIndex = 0;
groupBoxProjectTasks.TabStop = false;
groupBoxProjectTasks.Text = "Tasks";
//
// dataGridViewProjectTasks
//
dataGridViewProjectTasks.AllowUserToAddRows = false;
dataGridViewProjectTasks.AllowUserToDeleteRows = false;
dataGridViewProjectTasks.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
dataGridViewProjectTasks.Dock = DockStyle.Fill;
dataGridViewProjectTasks.Location = new Point(3, 35);
dataGridViewProjectTasks.Name = "dataGridViewProjectTasks";
dataGridViewProjectTasks.ReadOnly = true;
dataGridViewProjectTasks.RowHeadersWidth = 82;
dataGridViewProjectTasks.Size = new Size(1844, 433);
dataGridViewProjectTasks.TabIndex = 0;
//
// MainForm
//
AutoScaleDimensions = new SizeF(13F, 32F);
@ -202,6 +248,9 @@
MainForm_TabPage3.ResumeLayout(false);
tableLayoutPanelProjects1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)dataGridViewProjects).EndInit();
tableLayoutPanelProjects2.ResumeLayout(false);
groupBoxProjectTasks.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)dataGridViewProjectTasks).EndInit();
ResumeLayout(false);
PerformLayout();
}
@ -220,5 +269,8 @@
private TabPage MainForm_TabPage3;
private TableLayoutPanel tableLayoutPanelProjects1;
private DataGridView dataGridViewProjects;
private TableLayoutPanel tableLayoutPanelProjects2;
private GroupBox groupBoxProjectTasks;
private DataGridView dataGridViewProjectTasks;
}
}

View File

@ -114,7 +114,7 @@ namespace trakker
var selectedClient = dataGridViewClients.SelectedRows[0].DataBoundItem as Client;
if (selectedClient != null)
{
var dialog = new ClientForm(selectedClient);
var dialog = new ClientForm(selectedClient, _ctrl.GetLOV("state"));
if (dialog.ShowDialog(this) == DialogResult.OK)
{
Client client = dialog.Client;
@ -236,7 +236,7 @@ namespace trakker
var textColumn = new DataGridViewTextBoxColumn
{
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
DataPropertyName = "Status",
DataPropertyName = "StatusName",
Name = "Status",
Visible = true,
};
@ -250,7 +250,7 @@ namespace trakker
var selectedProject = dataGridViewProjects.SelectedRows[0].DataBoundItem as Project;
if (selectedProject != null)
{
var dialog = new ProjectForm(selectedProject, _ctrl.GetClients());
var dialog = new ProjectForm(selectedProject, _ctrl.GetClients(), _ctrl.GetLOV("project.status"));
if (dialog.ShowDialog(this) == DialogResult.OK)
{
Project project = dialog.Project;

View File

@ -73,7 +73,7 @@
groupBoxNewClient.Size = new Size(1144, 660);
groupBoxNewClient.TabIndex = 1;
groupBoxNewClient.TabStop = false;
groupBoxNewClient.Text = "New Client";
groupBoxNewClient.Text = "Project";
//
// tableLayoutPanel1
//

View File

@ -34,12 +34,15 @@ namespace trakker.Forms
/// </summary>
/// <param name="project">The <see cref="Project"/> instance to edit. Must not be null.</param>
/// <param name="clients">The list of <see cref="Client"/> instances to select from. Must not be null.</param>
public ProjectForm(Project project, BindingList<Client> clients)
/// <param name="status">The binding source for status values. Must not be null.</param>
public ProjectForm(Project project, BindingList<Client> clients, BindingSource status)
{
_project = project;
_clients = clients;
InitializeComponent();
Text = "Project Details";
comboBoxClient.DataSource = _clients;
comboBoxClient.DisplayMember = "Name";
comboBoxClient.ValueMember = "ClientId";
@ -58,7 +61,10 @@ namespace trakker.Forms
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);
comboBoxStatus.DataSource = status;
comboBoxStatus.DisplayMember = "Display";
comboBoxStatus.ValueMember = "Value";
comboBoxStatus.DataBindings.Add("SelectedValue", bindingSource, "Status", true);
richTextBoxNotes.DataBindings.Add("Text", bindingSource, "Notes", true);
// Configure dialog buttons and window behavior.

13
Models/LOV.cs Normal file
View File

@ -0,0 +1,13 @@
namespace trakker.Models
{
internal class LOV
{
public LOV(string value, string display)
{
Value = value;
Display = display;
}
public string Value { get; set; }
public string Display { get; set; }
}
}

View File

@ -23,6 +23,8 @@ namespace trakker.Models
public string Status { get; set; } = string.Empty;
public string StatusName { get; set; } = string.Empty;
public decimal? HourlyRate { get; set; }
public string? Notes { get; set; } = string.Empty;

View File

@ -25,6 +25,11 @@ namespace trakker.Services
LoadProjects();
}
public BindingSource GetLOV(string source)
{
var dbo = new Data.LOVData(_connectionString);
return new BindingSource { DataSource = dbo.Get(source) };
}
public BindingList<Client> GetClients()
{
var dbo = new Data.ClientData(_connectionString);