using BlueMine.Redmine; using System.Collections.ObjectModel; using System.Diagnostics; using System.Text.RegularExpressions; using System.Windows; using System.Windows.Input; using Wpf.Ui.Controls; using static BlueMine.Redmine.RedmineDto; namespace BlueMine { /// /// Interaction logic for MainWindow.xaml /// public partial class MainWindow : FluentWindow { private readonly RedmineManager _manager; private readonly SettingsManager _settings; private readonly RedmineConfig _config; private List _issues = []; public ObservableCollection IssuesList { get; set; } = []; public MainWindow(RedmineManager manager, SettingsManager settings, RedmineConfig config) { _settings = settings; _config = config; _manager = manager; InitializeComponent(); DataContext = this; } private async void WindowLoaded(object sender, RoutedEventArgs e) { apiUrlTextBox.Text = _config.RedmineUrl; apiPasswordBox.PlaceholderText = new string('●', _config.ApiKey.Length); if(await TestConnection()) { await LoadIssues(); await GetHours(); } } private void CalendarButtonClicked(object sender, RoutedEventArgs e) { flyoutCalendar.IsOpen = true; } private IProgress<(int, int)> UpdateProgress(string message) { var progress = new Progress<(int current, int total)>(); progress.ProgressChanged += (s, args) => { int current = args.current; int total = args.total; statusTextBlock.Text = $"{message}: {current} / {total}"; progressBar.Value = (double)current / total * 100; }; return progress; } private void ApiButtonClicked(object sender, RoutedEventArgs e) { apiFlyout.IsOpen = true; } private void CalendarSelectedDatesChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) { if(mainCalendar.SelectedDates.Count == 1) calendarButton.Content = mainCalendar.SelectedDate?.ToString("yyyy-MM-dd"); else if(mainCalendar.SelectedDates.Count > 1) calendarButton.Content = $"{mainCalendar.SelectedDates.Count} nap kiválasztva"; else calendarButton.Content = "Válassz egy napot"; } private void apiLinkButton_Click(object sender, RoutedEventArgs e) { string url = $"{_config.RedmineUrl}/my/account"; var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true }; Process.Start(psi); } private async void apiSaveButton_Click(object sender, RoutedEventArgs e) { _config.RedmineUrl = apiUrlTextBox.Text; _config.ApiKey = apiPasswordBox.Password; apiFlyout.IsOpen = false; if(await TestConnection()) { _settings.Save(_config); statusTextBlock.Text = "Beállítások mentve és kapcsolódva"; await LoadIssues(); await GetHours(); } else { _settings.Save(_config); statusTextBlock.Text = "Beállítások mentve, de a Redmine nem elérhető"; } } 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(); } private async void RefreshButtonClick(object sender, RoutedEventArgs e) { await LoadIssues(); await GetHours(); } private void BrowserButtonClick(object sender, RoutedEventArgs e) { var issueNum = IssueNumberTextBox.Text; if (int.TryParse(issueNum, out var issueId)) { string url = $"{_config.RedmineUrl}/issues/{issueId}"; var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true }; Process.Start(psi); } } 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(); } } await new Wpf.Ui.Controls.MessageBox { Title = "Hiba", Content = "Érvénytelen jegyszám.", }.ShowDialogAsync(); } private async void FixButtonClick(object sender, RoutedEventArgs e) { var progress = UpdateProgress("Idők javítása:"); var i = 0; foreach (var date in mainCalendar.SelectedDates) { var hours = 8 - await _manager.GetLoggedHoursAsync(date, date); if (hours <= 0) continue; var message = Constants.GenericMessages[Random.Shared.Next(Constants.GenericMessages.Length)]; var id = 801; await _manager.LogTimeAsync(id, hours, message, date); progress.Report((i, mainCalendar.SelectedDates.Count)); i++; } progressBar.Value = 0; await GetHours(); statusTextBlock.Text = "Idők javítva"; } private async void sendButton_Click(object sender, RoutedEventArgs e) { if(int.TryParse(IssueNumberTextBox.Text, out var issueId) && double.TryParse(HoursTextBox.Text, out var hours)) { if (hours * 4 != Math.Floor(hours * 4)) { await new Wpf.Ui.Controls.MessageBox { Title = "Idő formátum hiba", Content = "Az idő csak negyedórákra bontható le." }.ShowDialogAsync(); return; } var total = mainCalendar.SelectedDates.Count; var progress = UpdateProgress("Idők beköldése:"); for (int i = 0; i < total; i++) { await _manager.LogTimeAsync(issueId, hours, MessageTextBox.Text, mainCalendar.SelectedDates[i]); progress.Report((i, total)); } progressBar.Value = 0; statusTextBlock.Text = "Idők beküldve"; await GetHours(); } else { await new Wpf.Ui.Controls.MessageBox { Title = "Szám formátum hiba", Content = "Az idő/jegyszám nem rendes szám." }.ShowDialogAsync(); return; } } private void ListView_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) { var lv = sender as ListView; if(lv != null && lv.SelectedItem is IssueItem item) { IssueNumberTextBox.Text = item.IssueNumber.ToString(); } } } public partial class MainWindow : FluentWindow { private static readonly Regex _regexFractions = new Regex(@"^[0-9]*(?:\.[0-9]*)?$"); private static readonly Regex _regexNumbers = new Regex(@"^[0-9]*$"); private void FracValidation(object sender, TextCompositionEventArgs e) { var textBox = sender as TextBox; // Construct what the text WILL be if we allow this input string fullText = HoursTextBox.Text.Insert(HoursTextBox.CaretIndex, e.Text); // If the resulting text is not a match, block the input e.Handled = !_regexFractions.IsMatch(fullText); } private void FracPasting(object sender, DataObjectPastingEventArgs e) { if (e.DataObject.GetDataPresent(typeof(string))) { string text = (string)e.DataObject.GetData(typeof(string)); var textBox = sender as TextBox; string fullText = HoursTextBox.Text.Insert(HoursTextBox.CaretIndex, text); if (!_regexFractions.IsMatch(fullText)) e.CancelCommand(); } else e.CancelCommand(); } private void NumValidation(object sender, TextCompositionEventArgs e) { var textBox = sender as TextBox; // Construct what the text WILL be if we allow this input string fullText = HoursTextBox.Text.Insert(HoursTextBox.CaretIndex, e.Text); // If the resulting text is not a match, block the input e.Handled = !_regexNumbers.IsMatch(fullText); } private void NumPasting(object sender, DataObjectPastingEventArgs e) { if (e.DataObject.GetDataPresent(typeof(string))) { string text = (string)e.DataObject.GetData(typeof(string)); var textBox = sender as TextBox; string fullText = HoursTextBox.Text.Insert(HoursTextBox.CaretIndex, text); if (!_regexNumbers.IsMatch(fullText)) e.CancelCommand(); } else e.CancelCommand(); } 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)); var m = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1); var thisMonth = await _manager.GetLoggedHoursAsync(m, m.AddMonths(1).AddDays(-1)); todayTimeLabel.Text = today.ToString(); yesterdayTimeLabel.Text = yesterday.ToString(); monthTimeLabel.Text = thisMonth.ToString(); } 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) || issue.ProjectName.Contains(searchTextBox.Text, StringComparison.OrdinalIgnoreCase)); IssuesList.Clear(); foreach (var item in list) { IssuesList.Add(item); } } public async Task LoadIssues() { _issues.Clear(); _issues.AddRange(Constants.StaticTickets); _issues.AddRange(await _manager.GetCurrentIssuesAsync(UpdateProgress("Jegyek letöltése:"))); progressBar.Value = 0; FilterIssues(); statusTextBlock.Text = "Jegyek letöltve"; } public void DisableUi() { searchTextBox.IsEnabled = //issuesDataGrid.IsEnabled = IssueNumberTextBox.IsEnabled = HoursTextBox.IsEnabled = MessageTextBox.IsEnabled = calendarButton.IsEnabled = sendButton.IsEnabled = closeButton.IsEnabled = browserButton.IsEnabled = newButton.IsEnabled = refreshButton.IsEnabled = fixButton.IsEnabled = false; } public void EnableUi() { searchTextBox.IsEnabled = //issuesDataGrid.IsEnabled = IssueNumberTextBox.IsEnabled = HoursTextBox.IsEnabled = MessageTextBox.IsEnabled = calendarButton.IsEnabled = sendButton.IsEnabled = closeButton.IsEnabled = browserButton.IsEnabled = newButton.IsEnabled = refreshButton.IsEnabled = fixButton.IsEnabled = true; } public async Task TestConnection() { if (!await _manager.IsRedmineAvailable()) { 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; } } } }