Continued development
This commit is contained in:
parent
74b01247ed
commit
44864437a2
|
|
@ -36,22 +36,27 @@ namespace trakker.Data
|
|||
WITH RECURSIVE TaskHierarchy AS (
|
||||
-- Anchor: starting task(s)
|
||||
SELECT
|
||||
task_id,
|
||||
project_id,
|
||||
title,
|
||||
description,
|
||||
status,
|
||||
priority,
|
||||
due_date,
|
||||
estimated_hours,
|
||||
actual_hours,
|
||||
hourly_rate,
|
||||
parent_task_id,
|
||||
created_at,
|
||||
updated_at,
|
||||
t0.task_id,
|
||||
t0.project_id,
|
||||
t0.title,
|
||||
t0.description,
|
||||
t0.status,
|
||||
x0.display AS status_name,
|
||||
t0.priority,
|
||||
x1.display AS priority_name,
|
||||
t0.due_date,
|
||||
t0.estimated_hours,
|
||||
t0.actual_hours,
|
||||
t0.hourly_rate,
|
||||
(t0.actual_hours * t0.hourly_rate) AS amount,
|
||||
t0.parent_task_id,
|
||||
t0.created_at,
|
||||
t0.updated_at,
|
||||
0 AS level,
|
||||
title AS path
|
||||
FROM tasks
|
||||
t0.title AS path
|
||||
FROM tasks t0
|
||||
JOIN (SELECT value, display FROm lov WHERE source = 'task.status') x0 ON t0.status = x0.value
|
||||
JOIN (SELECT value, display FROm lov WHERE source = 'task.priority') x1 ON t0.priority = x1.value
|
||||
WHERE
|
||||
{whereClause}
|
||||
UNION ALL
|
||||
|
|
@ -63,17 +68,22 @@ namespace trakker.Data
|
|||
t.title,
|
||||
t.description,
|
||||
t.status,
|
||||
x0.display AS status_name,
|
||||
t.priority,
|
||||
x1.display AS priority_name,
|
||||
t.due_date,
|
||||
t.estimated_hours,
|
||||
t.actual_hours,
|
||||
t.hourly_rate,
|
||||
(t.actual_hours * t.hourly_rate) AS amount,
|
||||
t.parent_task_id,
|
||||
t.created_at,
|
||||
t.updated_at,
|
||||
th.level + 1,
|
||||
th.path || ' > ' || t.title
|
||||
FROM tasks t
|
||||
JOIN (SELECT value, display FROm lov WHERE source = 'task.status') x0 ON t.status = x0.value
|
||||
JOIN (SELECT value, display FROm lov WHERE source = 'task.priority') x1 ON t.priority = x1.value
|
||||
JOIN TaskHierarchy th ON t.parent_task_id = th.task_id
|
||||
)
|
||||
SELECT *
|
||||
|
|
@ -97,14 +107,17 @@ namespace trakker.Data
|
|||
var _var3 = reader.GetOrdinal("title");
|
||||
var _var4 = reader.GetOrdinal("description");
|
||||
var _var5 = reader.GetOrdinal("status");
|
||||
var _var6 = reader.GetOrdinal("priority");
|
||||
var _var7 = reader.GetOrdinal("due_date");
|
||||
var _var8 = reader.GetOrdinal("estimated_hours");
|
||||
var _var9 = reader.GetOrdinal("actual_hours");
|
||||
var _var10 = reader.GetOrdinal("hourly_rate");
|
||||
var _var11 = reader.GetOrdinal("parent_task_id");
|
||||
var _var12 = reader.GetOrdinal("created_at");
|
||||
var _var13 = reader.GetOrdinal("updated_at");
|
||||
var _var6 = reader.GetOrdinal("status_name");
|
||||
var _var7 = reader.GetOrdinal("priority");
|
||||
var _var8 = reader.GetOrdinal("priority_name");
|
||||
var _var9 = reader.GetOrdinal("due_date");
|
||||
var _var10 = reader.GetOrdinal("estimated_hours");
|
||||
var _var11 = reader.GetOrdinal("actual_hours");
|
||||
var _var12 = reader.GetOrdinal("hourly_rate");
|
||||
var _var13 = reader.GetOrdinal("amount");
|
||||
var _var14 = reader.GetOrdinal("parent_task_id");
|
||||
var _var15 = reader.GetOrdinal("created_at");
|
||||
var _var16 = reader.GetOrdinal("updated_at");
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
|
|
@ -115,14 +128,17 @@ namespace trakker.Data
|
|||
Title = reader.GetString(_var3),
|
||||
Description = reader.GetString(_var4),
|
||||
Status = reader.GetString(_var5),
|
||||
Priority = reader.GetString(_var6),
|
||||
DueDate = reader.IsDBNull(_var7) ? null : reader.GetDateTime(_var7),
|
||||
EstimatedHours = reader.IsDBNull(_var8) ? null : reader.GetDouble(_var8),
|
||||
ActualHours = reader.IsDBNull(_var9) ? null : reader.GetDouble(_var9),
|
||||
HourlyRate = reader.IsDBNull(_var10) ? null : reader.GetDouble(_var10),
|
||||
ParentTaskId = reader.IsDBNull(_var11) ? null : reader.GetString(_var11),
|
||||
CreatedAt = reader.IsDBNull(_var12) ? null : reader.GetDateTime(_var12),
|
||||
UpdatedAt = reader.IsDBNull(_var13) ? null : reader.GetDateTime(_var13),
|
||||
StatusName = reader.GetString(_var6),
|
||||
Priority = reader.GetString(_var7),
|
||||
PriorityName = reader.GetString(_var8),
|
||||
DueDate = reader.IsDBNull(_var9) ? null : reader.GetDateTime(_var9),
|
||||
EstimatedHours = reader.IsDBNull(_var10) ? null : reader.GetDouble(_var10),
|
||||
ActualHours = reader.IsDBNull(_var11) ? null : reader.GetDouble(_var11),
|
||||
HourlyRate = reader.IsDBNull(_var12) ? null : reader.GetDouble(_var12),
|
||||
Amount = reader.IsDBNull(_var13) ? null : reader.GetDouble(_var13),
|
||||
ParentTaskId = reader.IsDBNull(_var14) ? null : reader.GetString(_var14),
|
||||
CreatedAt = reader.IsDBNull(_var15) ? null : reader.GetDateTime(_var15),
|
||||
UpdatedAt = reader.IsDBNull(_var16) ? null : reader.GetDateTime(_var16),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -477,6 +493,11 @@ namespace trakker.Data
|
|||
using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = sql;
|
||||
|
||||
if (taskId != null)
|
||||
{
|
||||
cmd.Parameters.AddWithValue("$task_id", taskId);
|
||||
}
|
||||
|
||||
using var reader = cmd.ExecuteReader();
|
||||
|
||||
var _var1 = reader.GetOrdinal("task_comment_id");
|
||||
|
|
|
|||
|
|
@ -53,6 +53,11 @@
|
|||
addACommentToolStripMenuItem = new ToolStripMenuItem();
|
||||
splitContainerTasks2 = new SplitContainer();
|
||||
dataGridViewProjectTasks = new DataGridView();
|
||||
tableLayoutPanelTasks2 = new TableLayoutPanel();
|
||||
groupBoxTaskDescription = new GroupBox();
|
||||
richTextBoxTaskDescription = new RichTextBox();
|
||||
groupBoxTaskComments = new GroupBox();
|
||||
richTextBoxTaskComments = new RichTextBox();
|
||||
MainForm_MenuStrip.SuspendLayout();
|
||||
tabControlMainForm.SuspendLayout();
|
||||
MainForm_TabPage2.SuspendLayout();
|
||||
|
|
@ -70,8 +75,12 @@
|
|||
contextMenuStripTreeviewTasks.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)splitContainerTasks2).BeginInit();
|
||||
splitContainerTasks2.Panel1.SuspendLayout();
|
||||
splitContainerTasks2.Panel2.SuspendLayout();
|
||||
splitContainerTasks2.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)dataGridViewProjectTasks).BeginInit();
|
||||
tableLayoutPanelTasks2.SuspendLayout();
|
||||
groupBoxTaskDescription.SuspendLayout();
|
||||
groupBoxTaskComments.SuspendLayout();
|
||||
SuspendLayout();
|
||||
//
|
||||
// MainForm_MenuStrip
|
||||
|
|
@ -308,6 +317,10 @@
|
|||
// splitContainerTasks2.Panel1
|
||||
//
|
||||
splitContainerTasks2.Panel1.Controls.Add(dataGridViewProjectTasks);
|
||||
//
|
||||
// splitContainerTasks2.Panel2
|
||||
//
|
||||
splitContainerTasks2.Panel2.Controls.Add(tableLayoutPanelTasks2);
|
||||
splitContainerTasks2.Size = new Size(1234, 960);
|
||||
splitContainerTasks2.SplitterDistance = 411;
|
||||
splitContainerTasks2.TabIndex = 0;
|
||||
|
|
@ -325,6 +338,67 @@
|
|||
dataGridViewProjectTasks.Size = new Size(1234, 411);
|
||||
dataGridViewProjectTasks.TabIndex = 0;
|
||||
//
|
||||
// tableLayoutPanelTasks2
|
||||
//
|
||||
tableLayoutPanelTasks2.ColumnCount = 1;
|
||||
tableLayoutPanelTasks2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
|
||||
tableLayoutPanelTasks2.Controls.Add(groupBoxTaskDescription, 0, 0);
|
||||
tableLayoutPanelTasks2.Controls.Add(groupBoxTaskComments, 0, 1);
|
||||
tableLayoutPanelTasks2.Dock = DockStyle.Fill;
|
||||
tableLayoutPanelTasks2.Location = new Point(0, 0);
|
||||
tableLayoutPanelTasks2.Name = "tableLayoutPanelTasks2";
|
||||
tableLayoutPanelTasks2.RowCount = 2;
|
||||
tableLayoutPanelTasks2.RowStyles.Add(new RowStyle(SizeType.Percent, 25F));
|
||||
tableLayoutPanelTasks2.RowStyles.Add(new RowStyle(SizeType.Percent, 75F));
|
||||
tableLayoutPanelTasks2.Size = new Size(1234, 545);
|
||||
tableLayoutPanelTasks2.TabIndex = 0;
|
||||
//
|
||||
// groupBoxTaskDescription
|
||||
//
|
||||
groupBoxTaskDescription.Controls.Add(richTextBoxTaskDescription);
|
||||
groupBoxTaskDescription.Dock = DockStyle.Fill;
|
||||
groupBoxTaskDescription.Location = new Point(3, 3);
|
||||
groupBoxTaskDescription.Name = "groupBoxTaskDescription";
|
||||
groupBoxTaskDescription.Size = new Size(1228, 130);
|
||||
groupBoxTaskDescription.TabIndex = 0;
|
||||
groupBoxTaskDescription.TabStop = false;
|
||||
groupBoxTaskDescription.Text = "Description";
|
||||
//
|
||||
// richTextBoxTaskDescription
|
||||
//
|
||||
richTextBoxTaskDescription.BackColor = SystemColors.Control;
|
||||
richTextBoxTaskDescription.BorderStyle = BorderStyle.None;
|
||||
richTextBoxTaskDescription.Dock = DockStyle.Fill;
|
||||
richTextBoxTaskDescription.Location = new Point(3, 35);
|
||||
richTextBoxTaskDescription.Name = "richTextBoxTaskDescription";
|
||||
richTextBoxTaskDescription.ReadOnly = true;
|
||||
richTextBoxTaskDescription.Size = new Size(1222, 92);
|
||||
richTextBoxTaskDescription.TabIndex = 0;
|
||||
richTextBoxTaskDescription.Text = "";
|
||||
//
|
||||
// groupBoxTaskComments
|
||||
//
|
||||
groupBoxTaskComments.Controls.Add(richTextBoxTaskComments);
|
||||
groupBoxTaskComments.Dock = DockStyle.Fill;
|
||||
groupBoxTaskComments.Location = new Point(3, 139);
|
||||
groupBoxTaskComments.Name = "groupBoxTaskComments";
|
||||
groupBoxTaskComments.Size = new Size(1228, 403);
|
||||
groupBoxTaskComments.TabIndex = 1;
|
||||
groupBoxTaskComments.TabStop = false;
|
||||
groupBoxTaskComments.Text = "Comments";
|
||||
//
|
||||
// richTextBoxTaskComments
|
||||
//
|
||||
richTextBoxTaskComments.BackColor = SystemColors.Control;
|
||||
richTextBoxTaskComments.BorderStyle = BorderStyle.None;
|
||||
richTextBoxTaskComments.Dock = DockStyle.Fill;
|
||||
richTextBoxTaskComments.Location = new Point(3, 35);
|
||||
richTextBoxTaskComments.Name = "richTextBoxTaskComments";
|
||||
richTextBoxTaskComments.ReadOnly = true;
|
||||
richTextBoxTaskComments.Size = new Size(1222, 365);
|
||||
richTextBoxTaskComments.TabIndex = 1;
|
||||
richTextBoxTaskComments.Text = "";
|
||||
//
|
||||
// MainForm
|
||||
//
|
||||
AutoScaleDimensions = new SizeF(13F, 32F);
|
||||
|
|
@ -353,9 +427,13 @@
|
|||
splitContainerTasks1.ResumeLayout(false);
|
||||
contextMenuStripTreeviewTasks.ResumeLayout(false);
|
||||
splitContainerTasks2.Panel1.ResumeLayout(false);
|
||||
splitContainerTasks2.Panel2.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize)splitContainerTasks2).EndInit();
|
||||
splitContainerTasks2.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize)dataGridViewProjectTasks).EndInit();
|
||||
tableLayoutPanelTasks2.ResumeLayout(false);
|
||||
groupBoxTaskDescription.ResumeLayout(false);
|
||||
groupBoxTaskComments.ResumeLayout(false);
|
||||
ResumeLayout(false);
|
||||
PerformLayout();
|
||||
}
|
||||
|
|
@ -386,5 +464,10 @@
|
|||
private ToolStripMenuItem addACommentToolStripMenuItem;
|
||||
private SplitContainer splitContainerTasks2;
|
||||
private DataGridView dataGridViewProjectTasks;
|
||||
private TableLayoutPanel tableLayoutPanelTasks2;
|
||||
private GroupBox groupBoxTaskDescription;
|
||||
private GroupBox groupBoxTaskComments;
|
||||
private RichTextBox richTextBoxTaskDescription;
|
||||
private RichTextBox richTextBoxTaskComments;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using Microsoft.Data.Sqlite;
|
||||
using newcle.us.Forms;
|
||||
using newcle.us.Utilities;
|
||||
using System.ComponentModel;
|
||||
using trakker.Data;
|
||||
using trakker.Forms;
|
||||
|
|
@ -476,7 +478,36 @@ namespace trakker
|
|||
|
||||
private void addACommentToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
if (treeViewTasks1.SelectedNode == null) return;
|
||||
PTaskFS? selectedTask = treeViewTasks1.SelectedNode.Tag as PTaskFS;
|
||||
//MessageBox.Show(selectedTask != null ? selectedTask.ToString() : "No task selected", "Edit Task", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
if (selectedTask?.Parent == "/" || selectedTask?.GUID == "/")
|
||||
{
|
||||
MessageBox.Show("Cannot comment on root node", "Add Comment", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
TextAreaForm textAreaForm = new TextAreaForm("Add / Edit Comment");
|
||||
DialogResult result = textAreaForm.ShowDialog(this);
|
||||
if (result == DialogResult.OK)
|
||||
{
|
||||
if (string.IsNullOrEmpty(textAreaForm.Content)) return;
|
||||
TaskData taskData = new TaskData(connectionString);
|
||||
try
|
||||
{
|
||||
PTaskComment comment = new()
|
||||
{
|
||||
TaskId = selectedTask?.GUID ?? string.Empty,
|
||||
Comment = textAreaForm.Content ?? string.Empty,
|
||||
};
|
||||
taskData.SaveComment(comment);
|
||||
DialogExtensions.GenericSuccess($"Comment saved successfully for task '{selectedTask!.Node}'.");
|
||||
dataGridViewProjectTasks_SelectionChanged(null, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Error deleting task: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void InitDataGridViewProjectTasks()
|
||||
{
|
||||
|
|
@ -511,7 +542,7 @@ namespace trakker
|
|||
var textColumn = new DataGridViewTextBoxColumn
|
||||
{
|
||||
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
||||
DataPropertyName = "Status",
|
||||
DataPropertyName = "StatusName",
|
||||
Name = "Status",
|
||||
Visible = true,
|
||||
};
|
||||
|
|
@ -523,7 +554,7 @@ namespace trakker
|
|||
var textColumn = new DataGridViewTextBoxColumn
|
||||
{
|
||||
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
||||
DataPropertyName = "Priority",
|
||||
DataPropertyName = "PriorityName",
|
||||
Name = "Priority",
|
||||
Visible = true,
|
||||
};
|
||||
|
|
@ -586,52 +617,44 @@ namespace trakker
|
|||
dataGridViewProjectTasks.Columns.Add(textColumn);
|
||||
}
|
||||
|
||||
dataGridViewProjectTasks.Click += (s, e) =>
|
||||
// 7. Amount - Financial info at the end
|
||||
{
|
||||
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) =>
|
||||
{
|
||||
dataGridViewProjectTasks_SelectionChanged(s, e);
|
||||
};
|
||||
}
|
||||
public void dataGridViewProjectTasks_SelectionChanged(object? sender, EventArgs? e)
|
||||
{
|
||||
if (dataGridViewProjectTasks.SelectedRows.Count > 0)
|
||||
{
|
||||
var selectedIdx = dataGridViewProjectTasks.SelectedRows[0].Index;
|
||||
var selectedProject = dataGridViewProjectTasks.SelectedRows[0].DataBoundItem as Project;
|
||||
if (selectedProject != null)
|
||||
var selectedTask = dataGridViewProjectTasks.SelectedRows[0].DataBoundItem as PTask;
|
||||
if (selectedTask != 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
|
||||
// dataGridViewProjectTasks.ClearSelection();
|
||||
// if (selectedIdx >= 0 && selectedIdx < dataGridViewProjectTasks.Rows.Count)
|
||||
// {
|
||||
// dataGridViewProjectTasks.Rows[selectedIdx].Selected = true;
|
||||
// }
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// MessageBox.Show($"Error saving project: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
// }
|
||||
//}
|
||||
//return;
|
||||
richTextBoxTaskDescription.Text = selectedTask.Description;
|
||||
TaskData taskData = new TaskData(connectionString);
|
||||
List<PTaskComment> comments = taskData.GetComments(selectedTask.TaskId ?? string.Empty);
|
||||
richTextBoxTaskComments.Clear();
|
||||
foreach (var comment in comments)
|
||||
{
|
||||
richTextBoxTaskComments.AppendText($"{comment.CreatedAt}\n---\n{comment.Comment}\n\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//dataGridViewClients.SelectionChanged += (s, e) =>
|
||||
//{
|
||||
// if (dataGridViewClients.SelectedRows.Count > 0)
|
||||
// {
|
||||
// var selectedClient = dataGridViewClients.SelectedRows[0].DataBoundItem as Client;
|
||||
// if (selectedClient != null)
|
||||
// {
|
||||
// // Handle the selected client as needed
|
||||
// // MessageBox.Show($"Selected Client: {selectedClient.AddressStreet}", "Client Selected", MessageBoxButtons.OK, MessageBoxIcon.Information );
|
||||
// }
|
||||
// }
|
||||
//};
|
||||
|
||||
}
|
||||
public void FillDataGridViewProjectTasks(BindingSource tasks)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -383,12 +383,10 @@
|
|||
private GroupBox groupBoxNewTask;
|
||||
private TableLayoutPanel tableLayoutPanel1;
|
||||
private TableLayoutPanel tableLayoutPanel2;
|
||||
private Label labelClient;
|
||||
private Label labelTitle;
|
||||
private Label labelDescription;
|
||||
private Label labelHoursEst;
|
||||
private TextBox textBoxTitle;
|
||||
private ComboBox comboBoxClient;
|
||||
private TableLayoutPanel tableLayoutPanel6;
|
||||
private Label labelHoursActual;
|
||||
private RichTextBox richTextBoxDescription;
|
||||
|
|
|
|||
|
|
@ -25,9 +25,12 @@
|
|||
public string? Description { get; set; }
|
||||
|
||||
public string Status { get; set; } = "todo";
|
||||
public string StatusName { get; set; } = string.Empty;
|
||||
|
||||
public string Priority { get; set; } = "medium";
|
||||
|
||||
public string PriorityName { get; set; } = string.Empty;
|
||||
|
||||
public DateTime? DueDate { get; set; }
|
||||
|
||||
public double? EstimatedHours { get; set; } = 0;
|
||||
|
|
@ -36,6 +39,8 @@
|
|||
|
||||
public double? HourlyRate { get; set; } = 0;
|
||||
|
||||
public double? Amount { get; set; } = 0;
|
||||
|
||||
public string? ParentTaskId { get; set; }
|
||||
|
||||
public DateTime? CreatedAt { get; set; }
|
||||
|
|
@ -50,7 +55,9 @@
|
|||
Title: {Title}
|
||||
Description: {Description}
|
||||
Status: {Status}
|
||||
StatusName: {StatusName}
|
||||
Priority: {Priority}
|
||||
PriorityName: {PriorityName}
|
||||
DueDate: {DueDate}
|
||||
EstimatedHours: {EstimatedHours}
|
||||
ActualHours: {ActualHours}
|
||||
|
|
@ -96,6 +103,15 @@
|
|||
public string? Comment { get; set; } = string.Empty;
|
||||
|
||||
public DateTime? CreatedAt { get; set; }
|
||||
public override string ToString()
|
||||
{
|
||||
return @$"
|
||||
TaskCommentId: {TaskCommentId}
|
||||
TaskId: {TaskId}
|
||||
Comment: {Comment}
|
||||
CreatedAt: {CreatedAt}
|
||||
";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue