I finally realized I need to use the backtick and not the front tick to insert code!
herebelow is the code for a GanttChart in c# in a simplistic way. the code needs cleaning up, but it is working.
should there be something missing, please advise.
NB: if you change this into a component please share the code.
@page "/gantt-chart"
@using System.Net.Http
@using System.Threading.Tasks
@using System.Text.Json
@inject HttpClient Http
@inject GanttChartService ganttChartService
@inject TooltipService tooltipService
@using System.Linq
@using static GanttChart
<style>
body
{
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
width: 2000px;
}
.gantt-container
{
display: flex;
width: 100%;
height: 600px;
font-size: 10px;
}
.gantt-tasks,
.gantt-timeline
{
border: 1px solid #ccc;
overflow: auto;
position: relative;
}
.gantt-tasks
{
width: 35%;
}
.gantt-timeline
{
width: 65%;
}
table
{
border-collapse: collapse;
width: 100%;
}
th,
td
{
padding: 5px;
border: 1px solid #ccc;
white-space: nowrap;
text-align: left;
height: 30px;
color: black;
}
th:nth-child(4),
td:nth-child(4)
{
text-align: right;
/* width: 10px; */
}
th:nth-child(7),
td:nth-child(7)
{
text-align: right;
/* width: 10px; */
}
th
{
background: #333;
color: #fff;
position: sticky;
top: 0;
z-index: 10;
}
thead
{
height: 50px;
font-size: 14px;
}
tr
{
background-color: white;
}
.day-cell
{
text-align: center;
background: #f0f0f0;
font-size: 9px;
color: black;
writing-mode: vertical-rl;
}
.gantt-task
{
background-color: #3498db;
color: #fff;
text-align: center;
border-radius: 10px;
overflow: hidden;
position: absolute;
height: 25px;
padding-top: 5px;
top: 3px;
z-index: 1;
}
.gantt-task-container
{
position: relative;
height: 100%;
width: 100%;
}
.gantt-task-background
{
background-color: #3498db;
position: absolute;
height: 25px;
border-radius: 10px;
z-index: 2;
}
.gantt-task-text
{
position: absolute;
text-align: center;
margin-top: 7px;
margin-left: 10px;
z-index: 5;
opacity: 1 !important;
}
.gantt-task-progress
{
background-color: #2ecc71;
height: 25px;
border-radius: 10px 0 0 10px;
position: absolute;
z-index: 3;
}
.company-row
{
background: #f0f0f0;
cursor: pointer;
}
.collapse-icon
{
margin-right: 5px;
cursor: pointer;
}
.button-container
{
display: flex;
justify-content: flex-start;
margin-bottom: 10px;
}
.gantt-container
{
position: relative;
padding-top: 10px;
}
</style>
<PageTitle>GanttChart</PageTitle>
<RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" Gap="0.5rem" Class="rz-p-4 rz-mb-6 rz-border-radius-4" Style="border: var(--rz-grid-cell-border);width: 1440px">
<input type="text" @bind="searchQuery" placeholder="Search..." class="search-input ms-5" @oninput="@(async (e) => await FilterTasks())" />
<RadzenButton Click="ExpandAll" Text="Expand All" ButtonStyle="ButtonStyle.Primary" class="rz-border-radius-6" />
<RadzenButton Click="CollapseAll" Text="Collapse All" ButtonStyle="ButtonStyle.Primary" class="rz-border-radius-6" />
</RadzenStack>
<RadzenCard Variant="Variant.Filled" Class="rz-border-radius-5 rz-background-color-base-700 gantt-container" Style="overflow: auto;">
<RadzenStack Orientation="Orientation.Horizontal" JustifyContent="JustifyContent.Start" Gap="1rem" Style="width: 1500px">
<table class="table table-bordered">
<thead>
<tr>
<th>Priority Area</th>
<th>Main Topic</th>
<th>Expert</th>
<th>Days Allocated</th>
<th>Start</th>
<th>Deadline</th>
<th>% Completed</th>
<th>Status</th>
@for (int i = 0; i <= totalDays; i++)
{
var date = startDate.AddDays(i);
<th class="day-cell">@FormatDate(date)</th>
}
</tr>
</thead>
<tbody>
@foreach (var companyGroup in filteredTasks.GroupBy(t => t.Company))
{
<tr class="company-row" @onclick="() => ToggleCompany(companyGroup.Key)">
<td colspan="150"><span class="collapse-icon">@GetCollapseIcon(companyGroup.Key)</span>@companyGroup.Key</td>
@for (int i = 0; i <= totalDays; i++)
{
<td></td>
}
</tr>
@if (IsCompanyExpanded(companyGroup.Key))
{
@foreach (var task in companyGroup)
{
<tr>
<td>@task.PriorityArea</td>
<td>@task.MainTopic</td>
<td>@task.Expert</td>
<td>@task.Allocated</td>
<td>@task.Start.ToString("dd-MM-yy")</td>
<td>@task.Deadline.ToString("dd-MM-yy")</td>
<td>@task.PercentCompleted</td>
<td>@task.Status</td>
<td class="timeline-row" colspan="212" style="position: relative; height: 30px;">
@{
var date = startDate.AddDays(5);
var numfromstart = minStartDate * 30;
var tDuration = Math.Round(GetTaskDurationWidth(task) * 30);
var pDuration = Math.Round((tDuration * task.PercentCompleted) / 100);
}
<div class="gantt-task-container" style="left:@(GetDaysFromMinStartDate(task) * 100.0 / totalDays)%;">
<div class="gantt-task-background" Text="Left" style="width:@(tDuration)px;" >
</div>
<div class="gantt-task-progress" style="width:@(pDuration)px;"></div>
<div class="gantt-task-text">@(task.PercentCompleted) %</div>
</div>
</td>
</tr>
}
}
}
</tbody>
</table>
</RadzenStack>
</RadzenCard>
@code {
private List<GTask> tasks = new();
private List<GTask> filteredTasks = new();
private DateTime startDate = new DateTime(2024, 1, 1);
private DateTime endDate = new DateTime(2024, 7, 30);
private int totalDays => (endDate - startDate).Days;
private string searchQuery = "";
private IEnumerable<GChart.Models.GanttChart.BUTest> bUTests;
private HashSet<string> expandedCompanies = new HashSet<string>();
private int minStartDate = 0;
protected override async Task OnInitializedAsync()
{
try
{
bUTests = await ganttChartService.GetBUTests(new Query { Filter = $@"i => i.Company.Contains(@0) || i.PriorityArea.Contains(@0) || i.Expert.Contains(@0) || i.MainTopic.Contains(@0) || i.Status.Contains(@0)", FilterParameters = new object[] { searchQuery } });
tasks = ConvertBUTestToGTask(bUTests);
filteredTasks = new List<GTask>(tasks);
CreateTimelineData();
}
catch (Exception e)
{
Console.WriteLine($"Failed to load data: {e.Message}");
}
}
public List<GTask> ConvertBUTestToGTask(System.Collections.Generic.IEnumerable<GChart.Models.GanttChart.BUTest> bUTests)
{
return bUTests.Select(buTest => new GTask
{
Company = buTest.Company,
PriorityArea = buTest.PriorityArea,
MainTopic = buTest.MainTopic,
Expert = buTest.Expert,
Allocated = buTest.Allocated ?? 0,
Start = buTest.Start ?? DateTime.MinValue,
Deadline = buTest.Deadline ?? DateTime.MaxValue,
PercentCompleted = Math.Round(buTest.PercentCompleted * 100 ?? 0),
Status = buTest.Status,
IsExpanded = buTest.IsExpanded ?? true,
Timeline = new Dictionary<string, string>() // Initialize Timeline if needed
}).ToList();
}
private string FormatDate(DateTime date)
{
return date.ToString("dd-MM-yy");
}
private async Task FilterTasks()
{
filteredTasks = tasks.Where(task =>
task.GetType().GetProperties().Any(prop =>
prop.GetValue(task)?.ToString().ToLower().Contains(searchQuery.ToLower()) == true
)
).ToList();
CreateTimelineData();
}
private void CreateTimelineData()
{
foreach (var task in filteredTasks)
{
task.Timeline = new Dictionary<string, string>();
for (int i = 0; i <= totalDays; i++)
{
var date = startDate.AddDays(i);
if (date >= task.Start && date <= task.Deadline)
{
task.Timeline[date.ToString("dd-MM-yy")] = $"{task.PercentCompleted}%";
}
else
{
task.Timeline[date.ToString("dd-MM-yy")] = string.Empty;
}
}
}
}
private void ExpandAll()
{
expandedCompanies = new HashSet<string>(filteredTasks.Select(t => t.Company));
}
private void CollapseAll()
{
expandedCompanies.Clear();
}
private void ToggleCompany(string company)
{
if (expandedCompanies.Contains(company))
{
expandedCompanies.Remove(company);
}
else
{
expandedCompanies.Add(company);
}
}
private bool IsCompanyExpanded(string company)
{
return expandedCompanies.Contains(company);
}
private string GetCollapseIcon(string company)
{
return expandedCompanies.Contains(company) ? "▼" : "►";
}
private bool IsDateWithinTaskRange(DateTime date, GTask task)
{
return date >= task.Start && date <= task.Deadline;
}
private double GetTaskDurationWidth(GTask task)
{
var totalTaskDays = (task.Deadline - task.Start).Days + 1;
return totalTaskDays * 100.0 / totalDays;
}
private int GetDaysFromMinStartDate(GChart.Components.Pages.GanttChart.GTask task)
{
var minStartDate = filteredTasks.Min(t => t.Start);
return (task.Start - minStartDate).Days;
}
public class GTask
{
public string Company { get; set; }
public string PriorityArea { get; set; }
public string MainTopic { get; set; }
public string Expert { get; set; }
public int Allocated { get; set; }
public DateTime Start { get; set; }
public DateTime Deadline { get; set; }
public double PercentCompleted { get; set; }
public string Status { get; set; }
public bool IsExpanded { get; set; } = true;
public Dictionary<string, string> Timeline { get; set; } = new();
}
}