Compare commits

...

2 Commits

Author SHA1 Message Date
76cfb440b9 add hours to issue details (1.1.3) 2025-12-15 12:07:13 +01:00
ad6ca741e8 Remove unused usings 2025-12-15 11:22:21 +01:00
7 changed files with 141 additions and 7 deletions

View File

@@ -0,0 +1,105 @@
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
using System.Text.Json.Serialization;
namespace Blueberry.Redmine.Dto
{
public class TimeOnIssue
{
public class Activity
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
}
public class CustomField
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("value")]
public string Value { get; set; }
}
public class Issue
{
[JsonPropertyName("id")]
public int Id { get; set; }
}
public class Project
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
}
public class Root : IResponseList
{
[JsonPropertyName("time_entries")]
public List<TimeEntry> TimeEntries { get; set; }
[JsonPropertyName("total_count")]
public int TotalCount { get; set; }
[JsonPropertyName("offset")]
public int Offset { get; set; }
[JsonPropertyName("limit")]
public int Limit { get; set; }
}
public class TimeEntry
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("project")]
public Project Project { get; set; }
[JsonPropertyName("issue")]
public Issue Issue { get; set; }
[JsonPropertyName("user")]
public User User { get; set; }
[JsonPropertyName("activity")]
public Activity Activity { get; set; }
[JsonPropertyName("hours")]
public double Hours { get; set; }
[JsonPropertyName("comments")]
public string Comments { get; set; }
[JsonPropertyName("spent_on")]
public string SpentOn { get; set; }
[JsonPropertyName("created_on")]
public DateTime CreatedOn { get; set; }
[JsonPropertyName("updated_on")]
public DateTime UpdatedOn { get; set; }
[JsonPropertyName("custom_fields")]
public List<CustomField> CustomFields { get; set; }
}
public class User
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
}
}
}
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.

View File

@@ -232,6 +232,13 @@ namespace Blueberry.Redmine
await SendRequestAsync<object>(HttpMethod.Put, path, payload, token: token);
}
public async Task<List<TimeOnIssue.TimeEntry>> GetTimeOnIssue(int issueId, int limit = 25, IProgress<(int, int)>? progress = null, CancellationToken? token = null)
{
var path = $"time_entries.json?issue_id={issueId}";
var times = await SendRequestWithPagingAsync<TimeOnIssue.Root, TimeOnIssue.TimeEntry>(HttpMethod.Get, path, limit, (x)=>x.TimeEntries, progress, token: token);
return times;
}
public async Task<int> CreateNewIssue(int projectId, int trackerId, string subject, string description,
double estimatedHours, int priorityId, int? assigneeId = null, int? parentIssueId = null, CancellationToken? token = null)
{

View File

@@ -131,6 +131,12 @@ namespace Blueberry.Redmine
return await _apiClient.GetTrackersForProject(projectId.ToString(), token);
}
public async Task<List<TimeOnIssue.TimeEntry>> GetTimeOnIssue(int issueId, int limit = 25, IProgress<(int, int)>? progress = null, CancellationToken? token = null)
{
var result = await _apiClient.GetTimeOnIssue(issueId, limit, progress, token);
return result;
}
public async Task SetIssueStatusAsync(int issueId, int statusId, CancellationToken? token = null)
{
await _apiClient.SetIssueStatus(issueId, statusId, token);

View File

@@ -1,5 +1,4 @@
using Blueberry;
using Blueberry.Redmine;
using Blueberry.Redmine;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

View File

@@ -37,7 +37,8 @@ namespace Blueberry
iUpdatedTextBox.Text = _issue.UpdatedOn.ToString("yyyy-MM-dd");
iSpentTimeTextBox.Text = _issue.SpentHours.ToString();
journalProgressRing.Visibility = Visibility.Visible;
_journalDisplays.AddRange(await ProcessJournal(_issue.Journals));
var hours = await _manager.GetTimeOnIssue(_issue.Id);
_journalDisplays.AddRange(await ProcessJournal(_issue.Journals, hours));
if(!_journalDisplays.Any(x=>!x.IsData))
detailsToggleSwitch.IsChecked = true;
await LoadJournal();
@@ -75,7 +76,7 @@ namespace Blueberry
public partial class IssueWindow
{
public async Task<List<JournalDisplay>> ProcessJournal(IEnumerable<DetailedIssue.Journal> journals)
public async Task<List<JournalDisplay>> ProcessJournal(IEnumerable<DetailedIssue.Journal> journals, List<TimeOnIssue.TimeEntry> hours)
{
var js = new List<JournalDisplay>();
@@ -187,6 +188,24 @@ namespace Blueberry
} catch (Exception) { }
}
}
var totalHours = 0d;
hours = [.. hours.OrderBy(x => x.CreatedOn)];
foreach (var hour in hours)
{
totalHours += hour.Hours;
var user = hour.User.Name;
var date = hour.CreatedOn.ToString("yyyy-MM-dd HH:mm");
var content = $"Idő: {hour.Hours}\nEddig összesen: {totalHours}\n{hour.Comments}";
js.Add(new JournalDisplay
{
User = user,
Date = date,
Content = content,
IsData = true
});
}
js = [.. js.OrderBy(x => x.Date)];
return js;
}

View File

@@ -4,7 +4,6 @@ using Blueberry.Redmine.Dto;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Wpf.Ui.Controls;

View File

@@ -2,14 +2,13 @@
using System.IO;
using System.Net.Http;
using System.Text.Json;
using Windows.Media.Protection.PlayReady;
namespace Blueberry
{
public static class UpdateManager
{
private const string releaseUrl = "https://git.technopunk.space/api/v1/repos/tomi/Blueberry/releases/latest";
public const string CurrentVersion = "0.1.2";
public const string CurrentVersion = "0.1.3";
private static readonly string AppDir = AppDomain.CurrentDomain.BaseDirectory;
private static readonly HttpClient client = new();
public delegate void DownloadCompletedEventArgs();