complete refactor
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
using BlueMine.Redmine;
|
||||
using Blueberry;
|
||||
using Blueberry.Redmine;
|
||||
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;
|
||||
using static BlueMine.Redmine.RedmineDto;
|
||||
|
||||
namespace BlueMine
|
||||
{
|
||||
@@ -15,12 +17,13 @@ namespace BlueMine
|
||||
public partial class MainWindow : FluentWindow
|
||||
{
|
||||
private readonly RedmineManager _manager;
|
||||
private readonly SettingsManager _settings;
|
||||
private readonly RedmineSettingsManager _settings;
|
||||
private readonly RedmineConfig _config;
|
||||
private List<IssueItem> _issues = [];
|
||||
public ObservableCollection<IssueItem> IssuesList { get; set; } = [];
|
||||
private List<IssueList.Issue> _issues = [];
|
||||
public ObservableCollection<IssueList.Issue> IssuesList { get; set; } = [];
|
||||
public ObservableCollection<StatusList.IssueStatus> StatusList { get; set; } = [];
|
||||
|
||||
public MainWindow(RedmineManager manager, SettingsManager settings, RedmineConfig config)
|
||||
public MainWindow(RedmineManager manager, RedmineSettingsManager settings, RedmineConfig config)
|
||||
{
|
||||
_settings = settings;
|
||||
_config = config;
|
||||
@@ -33,11 +36,14 @@ namespace BlueMine
|
||||
{
|
||||
apiUrlTextBox.Text = _config.RedmineUrl;
|
||||
apiPasswordBox.PlaceholderText = new string('●', _config.ApiKey.Length);
|
||||
mainCalendar.SelectedDate = DateTime.Today;
|
||||
|
||||
if(await TestConnection())
|
||||
{
|
||||
await LoadIssues();
|
||||
await GetHours();
|
||||
Task loadIssuesTask = LoadIssues();
|
||||
Task getHoursTask = GetHours();
|
||||
|
||||
await Task.WhenAll(loadIssuesTask, getHoursTask);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +57,7 @@ namespace BlueMine
|
||||
var progress = new Progress<(int current, int total)>();
|
||||
progress.ProgressChanged += (s, args) =>
|
||||
{
|
||||
progressRing.Visibility = Visibility.Visible;
|
||||
int current = args.current;
|
||||
int total = args.total;
|
||||
statusTextBlock.Text = $"{message}: {current} / {total}";
|
||||
@@ -76,7 +83,7 @@ namespace BlueMine
|
||||
|
||||
private void apiLinkButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string url = $"{_config.RedmineUrl}/my/account";
|
||||
string url = $"{apiUrlTextBox.Text}/my/account";
|
||||
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
@@ -105,15 +112,6 @@ namespace BlueMine
|
||||
}
|
||||
}
|
||||
|
||||
private void DataGridSelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
|
||||
{
|
||||
/*if (issuesDataGrid.SelectedItem is IssueItem item)
|
||||
{
|
||||
IssueNumberTextBox.Text = item.IssueNumber.ToString();
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
private void SearchTextBoxTextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
|
||||
{
|
||||
FilterIssues();
|
||||
@@ -121,8 +119,10 @@ namespace BlueMine
|
||||
|
||||
private async void RefreshButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await LoadIssues();
|
||||
await GetHours();
|
||||
Task loadIssuesTask = LoadIssues();
|
||||
Task getHoursTask = GetHours();
|
||||
|
||||
await Task.WhenAll(loadIssuesTask, getHoursTask);
|
||||
}
|
||||
|
||||
private void BrowserButtonClick(object sender, RoutedEventArgs e)
|
||||
@@ -143,43 +143,23 @@ namespace BlueMine
|
||||
|
||||
private async void CloseButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var issueNum = IssueNumberTextBox.Text;
|
||||
if (int.TryParse(issueNum, out var issueId))
|
||||
{
|
||||
try
|
||||
{
|
||||
await _manager.CloseIssueAsync(issueId);
|
||||
await new Wpf.Ui.Controls.MessageBox
|
||||
{
|
||||
Title = "Sikeres művelet",
|
||||
Content = $"A(z) {issueId} számú jegy lezárva.",
|
||||
}.ShowDialogAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await new Wpf.Ui.Controls.MessageBox
|
||||
{
|
||||
Title = "Hiba",
|
||||
Content = $"A(z) {issueId} számú jegy lezárása sikertelen.",
|
||||
}.ShowDialogAsync();
|
||||
}
|
||||
}
|
||||
StatusList.Clear();
|
||||
var s = await _manager.GetStatusesAsync();
|
||||
foreach (var status in s)
|
||||
StatusList.Add(status);
|
||||
|
||||
await new Wpf.Ui.Controls.MessageBox
|
||||
{
|
||||
Title = "Hiba",
|
||||
Content = "Érvénytelen jegyszám.",
|
||||
}.ShowDialogAsync();
|
||||
statusFlyout.IsOpen = true;
|
||||
}
|
||||
|
||||
private async void FixButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var progress = UpdateProgress("Idők javítása:");
|
||||
progressRing.Visibility = Visibility.Visible;
|
||||
|
||||
var i = 0;
|
||||
foreach (var date in mainCalendar.SelectedDates)
|
||||
{
|
||||
var hours = 8 - await _manager.GetLoggedHoursAsync(date, date);
|
||||
var hours = 8 - await _manager.GetCurrentUserTimeAsync(date, date);
|
||||
if (hours <= 0)
|
||||
continue;
|
||||
var message = Constants.GenericMessages[Random.Shared.Next(Constants.GenericMessages.Length)];
|
||||
@@ -189,6 +169,7 @@ namespace BlueMine
|
||||
i++;
|
||||
}
|
||||
progressBar.Value = 0;
|
||||
progressRing.Visibility = Visibility.Hidden;
|
||||
await GetHours();
|
||||
statusTextBlock.Text = "Idők javítva";
|
||||
|
||||
@@ -196,6 +177,16 @@ namespace BlueMine
|
||||
|
||||
private async void sendButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if(mainCalendar.SelectedDates.Count == 0)
|
||||
{
|
||||
await new Wpf.Ui.Controls.MessageBox
|
||||
{
|
||||
Title = "Nap hiba",
|
||||
Content = "Nincs kijelölve nap."
|
||||
}.ShowDialogAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
if(int.TryParse(IssueNumberTextBox.Text, out var issueId)
|
||||
&& double.TryParse(HoursTextBox.Text, out var hours))
|
||||
{
|
||||
@@ -209,16 +200,17 @@ namespace BlueMine
|
||||
return;
|
||||
}
|
||||
var total = mainCalendar.SelectedDates.Count;
|
||||
var progress = UpdateProgress("Idők beköldése:");
|
||||
var progress = UpdateProgress("Idők beküldése:");
|
||||
progressRing.Visibility = Visibility.Visible;
|
||||
for (int i = 0; i < total; i++)
|
||||
{
|
||||
await _manager.LogTimeAsync(issueId, hours, MessageTextBox.Text, mainCalendar.SelectedDates[i]);
|
||||
progress.Report((i, total));
|
||||
progress.Report((i + 1, total));
|
||||
}
|
||||
progressBar.Value = 0;
|
||||
statusTextBlock.Text = "Idők beküldve";
|
||||
|
||||
await GetHours();
|
||||
progressBar.Value = 0;
|
||||
progressRing.Visibility = Visibility.Hidden;
|
||||
statusTextBlock.Text = "Idők beküldve";
|
||||
} else
|
||||
{
|
||||
await new Wpf.Ui.Controls.MessageBox
|
||||
@@ -233,11 +225,128 @@ namespace BlueMine
|
||||
private void ListView_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
|
||||
{
|
||||
var lv = sender as ListView;
|
||||
if(lv != null && lv.SelectedItem is IssueItem item)
|
||||
if(lv != null && lv.SelectedItem is IssueList.Issue item)
|
||||
{
|
||||
IssueNumberTextBox.Text = item.IssueNumber.ToString();
|
||||
IssueNumberTextBox.Text = item.Id.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private async void statusSaveButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var issueNum = IssueNumberTextBox.Text;
|
||||
if (int.TryParse(issueNum, out var issueId))
|
||||
{
|
||||
try
|
||||
{
|
||||
var status = statusComboBox.SelectedItem as StatusList.IssueStatus;
|
||||
|
||||
if (status == null)
|
||||
{
|
||||
await new Wpf.Ui.Controls.MessageBox
|
||||
{
|
||||
Title = "Érvénytelen státusz",
|
||||
Content = "Státusz kiválasztása sikertelen."
|
||||
}.ShowDialogAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
await _manager.SetIssueStatusAsync(issueId, status.Id);
|
||||
|
||||
var oldIssue = IssuesList.First(x=>x.Id == issueId);
|
||||
var newIssue = await _manager.GetSimpleIssueAsync(issueId);
|
||||
var index = IssuesList.IndexOf(oldIssue);
|
||||
IssuesList.Insert(index, newIssue);
|
||||
IssuesList.Remove(oldIssue);
|
||||
|
||||
await new Wpf.Ui.Controls.MessageBox
|
||||
{
|
||||
Title = "Sikeres művelet",
|
||||
Content = $"A(z) {issueId} számú jegy új státusza: {status.Name}.",
|
||||
}.ShowDialogAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await new Wpf.Ui.Controls.MessageBox
|
||||
{
|
||||
Title = "Hiba",
|
||||
Content = $"A(z) {issueId} számú jegy módosítása sikertelen.",
|
||||
}.ShowDialogAsync();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await new Wpf.Ui.Controls.MessageBox
|
||||
{
|
||||
Title = "Hiba",
|
||||
Content = "Érvénytelen jegyszám.",
|
||||
}.ShowDialogAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async void searchTextBox_KeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
if(e.Key == Key.Enter && searchTextBox.Text.Length > 0)
|
||||
{
|
||||
if (int.TryParse(searchTextBox.Text, out var issueId))
|
||||
{
|
||||
try
|
||||
{
|
||||
statusTextBlock.Text = "Jegy keresése...";
|
||||
progressRing.Visibility = Visibility.Visible;
|
||||
var issue = await _manager.GetSimpleIssueAsync(issueId);
|
||||
IssuesList.Clear();
|
||||
IssuesList.Add(issue);
|
||||
statusTextBlock.Text = "Jegy betöltve";
|
||||
progressRing.Visibility = Visibility.Hidden;
|
||||
} catch (Exception) {
|
||||
statusTextBlock.Text = "Jegy nem található";
|
||||
progressRing.Visibility = Visibility.Hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void openTicketButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is FrameworkElement button && button.DataContext is IssueList.Issue item)
|
||||
{
|
||||
// 2. Access the property directly from your model
|
||||
var issueId = item.Id;
|
||||
|
||||
try
|
||||
{
|
||||
statusTextBlock.Text = "Jegy betöltése...";
|
||||
progressRing.Visibility = Visibility.Visible;
|
||||
var issue = await _manager.GetIssueAsync(issueId);
|
||||
statusTextBlock.Text = "Jegy betöltve";
|
||||
progressRing.Visibility = Visibility.Hidden;
|
||||
var issueWindow = new IssueWindow(issue, _manager, _config);
|
||||
issueWindow.Show();
|
||||
} catch (Exception)
|
||||
{
|
||||
|
||||
statusTextBlock.Text = "Jegy betöltés sikertelen";
|
||||
progressRing.Visibility = Visibility.Hidden;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private async void trackerButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (int.TryParse(IssueNumberTextBox.Text, out var issueId))
|
||||
await OpenTimeTracker(issueId);
|
||||
|
||||
}
|
||||
|
||||
private async void hoursButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await new Wpf.Ui.Controls.MessageBox
|
||||
{
|
||||
Title = "Under construction"
|
||||
}.ShowDialogAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public partial class MainWindow : FluentWindow
|
||||
@@ -295,22 +404,43 @@ namespace BlueMine
|
||||
|
||||
public async Task GetHours()
|
||||
{
|
||||
var today = await _manager.GetLoggedHoursAsync(DateTime.Today, DateTime.Today);
|
||||
var yesterday = await _manager.GetLoggedHoursAsync(DateTime.Today.AddDays(-1), DateTime.Today.AddDays(-1));
|
||||
todayProgressRing.Visibility =
|
||||
yesterdayProgressRing.Visibility =
|
||||
monthProgressRing.Visibility = Visibility.Visible;
|
||||
var today = await _manager.GetCurrentUserTimeTodayAsync();
|
||||
var yesterday = await _manager.GetCurrentUserTimeYesterdayAsync();
|
||||
var thisMonth = await _manager.GetCurrentUserTimeThisMonthAsync();
|
||||
|
||||
var m = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
|
||||
var thisMonth = await _manager.GetLoggedHoursAsync(m, m.AddMonths(1).AddDays(-1));
|
||||
int workingDays = 0;
|
||||
DateTime currentDate = DateTime.Today;
|
||||
|
||||
for (int day = 1; day <= currentDate.Day; day++)
|
||||
{
|
||||
var dateToCheck = new DateTime(currentDate.Year, currentDate.Month, day);
|
||||
if (dateToCheck.DayOfWeek != DayOfWeek.Saturday &&
|
||||
dateToCheck.DayOfWeek != DayOfWeek.Sunday)
|
||||
{
|
||||
workingDays++;
|
||||
}
|
||||
}
|
||||
|
||||
var avgHours = Math.Round(thisMonth/workingDays, 2);
|
||||
|
||||
todayTimeLabel.Text = today.ToString();
|
||||
yesterdayTimeLabel.Text = yesterday.ToString();
|
||||
monthTimeLabel.Text = thisMonth.ToString();
|
||||
averageTimeLabel.Text = avgHours.ToString();
|
||||
|
||||
todayProgressRing.Visibility =
|
||||
yesterdayProgressRing.Visibility =
|
||||
monthProgressRing.Visibility = Visibility.Hidden;
|
||||
}
|
||||
public void FilterIssues()
|
||||
{
|
||||
var list = string.IsNullOrWhiteSpace(searchTextBox.Text)
|
||||
? _issues
|
||||
: _issues.Where(issue => issue.IssueName.Contains(searchTextBox.Text, StringComparison.OrdinalIgnoreCase)
|
||||
|| issue.IssueNumber.ToString().Contains(searchTextBox.Text)
|
||||
: _issues.Where(issue => issue.Subject.Contains(searchTextBox.Text, StringComparison.OrdinalIgnoreCase)
|
||||
|| issue.Id.ToString().Contains(searchTextBox.Text)
|
||||
|| issue.ProjectName.Contains(searchTextBox.Text, StringComparison.OrdinalIgnoreCase));
|
||||
IssuesList.Clear();
|
||||
foreach (var item in list)
|
||||
@@ -321,9 +451,14 @@ namespace BlueMine
|
||||
public async Task LoadIssues()
|
||||
{
|
||||
_issues.Clear();
|
||||
_issues.AddRange(Constants.StaticTickets);
|
||||
_issues.AddRange(await _manager.GetCurrentIssuesAsync(UpdateProgress("Jegyek letöltése:")));
|
||||
statusTextBlock.Text = "Jegyek letöltése...";
|
||||
progressRing.Visibility = Visibility.Visible;
|
||||
foreach (var issueId in Constants.StaticTickets)
|
||||
_issues.Add(await _manager.GetSimpleIssueAsync(issueId));
|
||||
|
||||
_issues.AddRange(await _manager.GetCurrentUserIssuesAsync(progress: UpdateProgress("Jegyek letöltése:")));
|
||||
progressBar.Value = 0;
|
||||
progressRing.Visibility = Visibility.Hidden;
|
||||
FilterIssues();
|
||||
statusTextBlock.Text = "Jegyek letöltve";
|
||||
}
|
||||
@@ -359,19 +494,49 @@ namespace BlueMine
|
||||
}
|
||||
public async Task<bool> TestConnection()
|
||||
{
|
||||
if (!await _manager.IsRedmineAvailable())
|
||||
statusTextBlock.Text = $"Kapcsolódás Redminehoz...";
|
||||
|
||||
int maxRetries = 3;
|
||||
int timeoutSeconds = 1; // Force kill after 5s
|
||||
|
||||
for (int i = 0; i < maxRetries; i++)
|
||||
{
|
||||
DisableUi();
|
||||
apiButton.Appearance = Wpf.Ui.Controls.ControlAppearance.Primary;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
EnableUi();
|
||||
apiButton.Appearance = Wpf.Ui.Controls.ControlAppearance.Secondary;
|
||||
statusTextBlock.Text = "Kapcsolódva";
|
||||
return true;
|
||||
try
|
||||
{
|
||||
// Creates a token that cancels automatically
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds));
|
||||
|
||||
// Pass the token. If it hangs, this throws OperationCanceledException
|
||||
if (await _manager.IsRedmineAvailable(cts.Token))
|
||||
{
|
||||
EnableUi();
|
||||
apiButton.Appearance = ControlAppearance.Secondary;
|
||||
statusTextBlock.Text = "Kapcsolódva";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Ignore timeout/error and try again unless it's the last attempt
|
||||
if (i == maxRetries - 1) break;
|
||||
}
|
||||
|
||||
statusTextBlock.Text = $"Kapcsolódási hiba. Újrapróbálkozás: {i + 1}/{maxRetries}";
|
||||
|
||||
// Wait 1 second before retrying
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
|
||||
// All attempts failed
|
||||
DisableUi();
|
||||
apiButton.Appearance = ControlAppearance.Primary;
|
||||
return false;
|
||||
}
|
||||
public async Task OpenTimeTracker(int issueId)
|
||||
{
|
||||
var i = await _manager.GetSimpleIssueAsync(issueId);
|
||||
var timer = new TimeTrackerWindow(_config, _manager, i);
|
||||
timer.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user