diff --git a/OutlookCaseHelper/Form1.cs b/OutlookCaseHelper/Form1.cs index 28c859c..e290a42 100644 --- a/OutlookCaseHelper/Form1.cs +++ b/OutlookCaseHelper/Form1.cs @@ -26,6 +26,7 @@ namespace OutlookCaseHelper { trayMenu = new ContextMenuStrip(); trayMenu.Items.Add("Create Rule (Selected Email)", null, ProcessEmail_Click); + trayMenu.Items.Add("Create Rule (Manual ID)", null, CreateRuleManual_Click); trayMenu.Items.Add("Remove Rule (Selected Email)", null, RemoveRuleFromSelected_Click); trayMenu.Items.Add("Remove Rule (Manual ID)", null, RemoveRule_Click); trayMenu.Items.Add("-"); @@ -42,17 +43,14 @@ namespace OutlookCaseHelper private void InitializeTimer() { monitorTimer = new System.Windows.Forms.Timer(); - monitorTimer.Interval = 60000; // verifica a cada 60 segundos + monitorTimer.Interval = 60000; monitorTimer.Tick += MonitorTimer_Tick; monitorTimer.Start(); } private void MonitorTimer_Tick(object? sender, EventArgs e) { - try - { - outlookHelper.ProcessActiveRules(); - } + try { outlookHelper.ProcessActiveRules(); } catch { } } @@ -65,30 +63,93 @@ namespace OutlookCaseHelper { MessageBox.Show( "No email selected or ID not found in subject.\n\nExpected format: Title - TrackingID#1111111111111111", - "Warning", - MessageBoxButtons.OK, - MessageBoxIcon.Warning); + "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } - bool success = outlookHelper.CreateFolderAndMoveEmails(trackingId); + // Verifica se já existe regra ativa + if (outlookHelper.FindRuleByTrackingId(trackingId) != null) + { + MessageBox.Show($"Rule for TrackingID#{trackingId} already exists!", + "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + // Verifica se existe em Closed — reabre sem pedir nome + if (outlookHelper.ExistsInClosed(trackingId)) + { + var triggerEmail = outlookHelper.GetSelectedEmail(); + bool reopened = outlookHelper.ReopenFromClosed(trackingId, triggerEmail); + if (reopened) + MessageBox.Show( + $"Case reopened! Folder moved from Closed to Active.\n\nTrackingID: {trackingId}", + "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); + else + MessageBox.Show("Error reopening case.", + "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + // Caso novo — pede nome + var ruleForm = new CreateRuleForm(trackingId, readonlyId: true); + if (ruleForm.ShowDialog() != DialogResult.OK) return; + + string folderName = ruleForm.FolderName; + + bool success = outlookHelper.CreateFolderAndMoveEmails(trackingId, folderName); if (success) - { - var activeCount = outlookHelper.GetActiveRules().Count; MessageBox.Show( - $"Rule created! Emails moved and monitoring started.\n\nTrackingID: {trackingId}\nActive rules: {activeCount}", - "Success", - MessageBoxButtons.OK, - MessageBoxIcon.Information); - } + $"Rule created! Emails moved and monitoring started.\n\nFolder: {folderName}\nActive rules: {outlookHelper.GetActiveRules().Count}", + "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); else + MessageBox.Show("Error processing email. Make sure Outlook is open.", + "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + catch (Exception ex) + { + MessageBox.Show($"Error: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void CreateRuleManual_Click(object? sender, EventArgs e) + { + try + { + var ruleForm = new CreateRuleForm("", readonlyId: false); + if (ruleForm.ShowDialog() != DialogResult.OK) return; + + string trackingId = ruleForm.TrackingId; + string folderName = ruleForm.FolderName; + + if (outlookHelper.FindRuleByTrackingId(trackingId) != null) { - MessageBox.Show( - "Error processing email. Make sure Outlook is open.", - "Error", - MessageBoxButtons.OK, - MessageBoxIcon.Error); + MessageBox.Show($"Rule for TrackingID#{trackingId} already exists!", + "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; } + + // Verifica se existe em Closed — reabre sem pedir nome + if (outlookHelper.ExistsInClosed(trackingId)) + { + bool reopened = outlookHelper.ReopenFromClosed(trackingId); + if (reopened) + MessageBox.Show( + $"Case reopened! Folder moved from Closed to Active.\n\nTrackingID: {trackingId}", + "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); + else + MessageBox.Show("Error reopening case.", + "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + bool success = outlookHelper.CreateFolderAndMoveEmails(trackingId, folderName); + if (success) + MessageBox.Show( + $"Rule created! Emails moved and monitoring started.\n\nFolder: {folderName}\nActive rules: {outlookHelper.GetActiveRules().Count}", + "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); + else + MessageBox.Show("Error creating rule. Make sure Outlook is open.", + "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } catch (Exception ex) { @@ -101,25 +162,24 @@ namespace OutlookCaseHelper try { var form = new InputForm("Enter TrackingID to remove:", "Remove Rule"); - if (form.ShowDialog() == DialogResult.OK && !string.IsNullOrEmpty(form.TrackingId)) + if (form.ShowDialog() == DialogResult.OK && !string.IsNullOrEmpty(form.Value)) { - bool success = outlookHelper.RemoveRuleAndMoveToClosed(form.TrackingId); + string? folderName = outlookHelper.FindRuleByTrackingId(form.Value.Trim()); + if (folderName == null) + { + MessageBox.Show($"No active rule found for TrackingID#{form.Value}.", + "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + bool success = outlookHelper.RemoveRuleAndMoveToClosed(folderName); if (success) - { MessageBox.Show( - "Rule removed!\n\nFolder moved to: Inbox > Cases > Closed\nMonitoring stopped.", - "Success", - MessageBoxButtons.OK, - MessageBoxIcon.Information); - } + $"Rule removed!\n\nFolder moved to: Inbox > Cases > Closed\nFolder: {folderName}\nMonitoring stopped.", + "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); else - { - MessageBox.Show( - "Error removing rule. Check if folder exists.", - "Error", - MessageBoxButtons.OK, - MessageBoxIcon.Error); - } + MessageBox.Show("Error removing rule. Check if folder exists.", + "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } catch (Exception ex) @@ -137,29 +197,26 @@ namespace OutlookCaseHelper { MessageBox.Show( "No email selected or ID not found in subject.\n\nExpected format: Title - TrackingID#1111111111111111", - "Warning", - MessageBoxButtons.OK, - MessageBoxIcon.Warning); + "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } - bool success = outlookHelper.RemoveRuleAndMoveToClosed(trackingId); + string? folderName = outlookHelper.FindRuleByTrackingId(trackingId); + if (folderName == null) + { + MessageBox.Show($"No active rule found for TrackingID#{trackingId}.", + "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + bool success = outlookHelper.RemoveRuleAndMoveToClosed(folderName); if (success) - { MessageBox.Show( - $"Rule removed!\n\nFolder moved to: Inbox > Cases > Closed\nTrackingID: {trackingId}\nMonitoring stopped.", - "Success", - MessageBoxButtons.OK, - MessageBoxIcon.Information); - } + $"Rule removed!\n\nFolder moved to: Inbox > Cases > Closed\nFolder: {folderName}\nMonitoring stopped.", + "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); else - { - MessageBox.Show( - "Error removing rule. Check if folder exists and Outlook is open.", - "Error", - MessageBoxButtons.OK, - MessageBoxIcon.Error); - } + MessageBox.Show("Error removing rule. Check if folder exists and Outlook is open.", + "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } catch (Exception ex) { @@ -179,36 +236,105 @@ namespace OutlookCaseHelper Application.Exit(); } + private class CreateRuleForm : Form + { + private TextBox txtId; + private TextBox txtName; + private Label lblPreview; + public string TrackingId { get; private set; } = ""; + public string FolderName { get; private set; } = ""; + + public CreateRuleForm(string trackingId, bool readonlyId) + { + this.Text = "Create Rule"; + this.Size = new Size(420, 230); + this.MinimumSize = new Size(420, 230); + this.MaximumSize = new Size(420, 230); + this.FormBorderStyle = FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.StartPosition = FormStartPosition.CenterScreen; + + var lblId = new Label { Text = "TrackingID (required):", Left = 20, Top = 15, Width = 370, Height = 20 }; + txtId = new TextBox { Left = 20, Top = 38, Width = 370, Height = 24, Text = trackingId, ReadOnly = readonlyId }; + if (readonlyId) txtId.BackColor = System.Drawing.SystemColors.Control; + + var lblName = new Label { Text = "Additional name (optional):", Left = 20, Top = 72, Width = 370, Height = 20 }; + txtName = new TextBox { Left = 20, Top = 95, Width = 370, Height = 24 }; + + lblPreview = new Label { Left = 20, Top = 128, Width = 370, Height = 20, ForeColor = System.Drawing.Color.Gray }; + UpdatePreview(trackingId); + + var btnOk = new Button { Text = "OK", Left = 220, Top = 158, Width = 80, DialogResult = DialogResult.OK }; + var btnCancel = new Button { Text = "Cancel", Left = 310, Top = 158, Width = 80, DialogResult = DialogResult.Cancel }; + + txtId.TextChanged += (s, e) => UpdatePreview(txtId.Text.Trim()); + txtName.TextChanged += (s, e) => UpdatePreview(txtId.Text.Trim()); + + this.Controls.AddRange(new Control[] { lblId, txtId, lblName, txtName, lblPreview, btnOk, btnCancel }); + this.AcceptButton = btnOk; + this.CancelButton = btnCancel; + } + + private void UpdatePreview(string id) + { + string name = txtName?.Text.Trim() ?? ""; + string preview = string.IsNullOrEmpty(name) ? id : $"{id} | {name}"; + if (lblPreview != null) + lblPreview.Text = $"Folder: {preview}"; + } + + protected override void OnFormClosing(FormClosingEventArgs e) + { + if (this.DialogResult == DialogResult.OK) + { + TrackingId = txtId.Text.Trim(); + string name = txtName.Text.Trim(); + + if (string.IsNullOrEmpty(TrackingId)) + { + MessageBox.Show("TrackingID is required!", "Validation", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + e.Cancel = true; + return; + } + + FolderName = string.IsNullOrEmpty(name) ? TrackingId : $"{TrackingId} | {name}"; + } + base.OnFormClosing(e); + } + } + private class InputForm : Form { private TextBox txtInput; - public string TrackingId { get; private set; } = ""; + public string Value { get; private set; } = ""; public InputForm(string prompt, string title) { this.Text = title; - this.Width = 400; - this.Height = 150; + this.Size = new Size(380, 150); + this.MinimumSize = new Size(380, 150); + this.MaximumSize = new Size(380, 150); + this.FormBorderStyle = FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; this.StartPosition = FormStartPosition.CenterScreen; - var label = new Label { Text = prompt, Left = 20, Top = 20, Width = 360, Height = 30 }; - txtInput = new TextBox { Left = 20, Top = 60, Width = 340, Height = 30 }; + var label = new Label { Text = prompt, Left = 20, Top = 15, Width = 330, Height = 25 }; + txtInput = new TextBox { Left = 20, Top = 45, Width = 330, Height = 24 }; - var btnOk = new Button { Text = "OK", Left = 200, Top = 100, Width = 80, DialogResult = DialogResult.OK }; - var btnCancel = new Button { Text = "Cancel", Left = 290, Top = 100, Width = 80, DialogResult = DialogResult.Cancel }; - - this.Controls.Add(label); - this.Controls.Add(txtInput); - this.Controls.Add(btnOk); - this.Controls.Add(btnCancel); + var btnOk = new Button { Text = "OK", Left = 155, Top = 80, Width = 80, DialogResult = DialogResult.OK }; + var btnCancel = new Button { Text = "Cancel", Left = 245, Top = 80, Width = 80, DialogResult = DialogResult.Cancel }; + this.Controls.AddRange(new Control[] { label, txtInput, btnOk, btnCancel }); this.AcceptButton = btnOk; this.CancelButton = btnCancel; } protected override void OnFormClosing(FormClosingEventArgs e) { - TrackingId = txtInput.Text; + Value = txtInput.Text; base.OnFormClosing(e); } } diff --git a/OutlookCaseHelper/OutlookHelper.cs b/OutlookCaseHelper/OutlookHelper.cs index 24c30a1..f78de91 100644 --- a/OutlookCaseHelper/OutlookHelper.cs +++ b/OutlookCaseHelper/OutlookHelper.cs @@ -44,7 +44,6 @@ namespace OutlookCaseHelper inboxItems = inboxFolder.Items; inboxItems.ItemAdd += OnEmailReceived; } - if (sentFolder != null) { sentItems = sentFolder.Items; @@ -62,8 +61,9 @@ namespace OutlookCaseHelper if (item is not Outlook.MailItem mail) return; if (mail.Subject == null) return; - foreach (var trackingId in activeRules) + foreach (var folderName in activeRules) { + string trackingId = ExtractTrackingId(folderName); if (mail.Subject.Contains($"TrackingID#{trackingId}")) { Outlook.Folder? inboxFolder = @@ -72,7 +72,7 @@ namespace OutlookCaseHelper Outlook.Folder casesFolder = GetOrCreateFolder(inboxFolder, "Cases"); Outlook.Folder activeFolder = GetOrCreateFolder(casesFolder, "Active"); - Outlook.Folder trackingFolder = GetOrCreateFolder(activeFolder, trackingId); + Outlook.Folder trackingFolder = GetOrCreateFolder(activeFolder, folderName); mail.Move(trackingFolder); break; @@ -90,8 +90,9 @@ namespace OutlookCaseHelper if (item is not Outlook.MailItem mail) return; if (mail.Subject == null) return; - foreach (var trackingId in activeRules) + foreach (var folderName in activeRules) { + string trackingId = ExtractTrackingId(folderName); if (mail.Subject.Contains($"TrackingID#{trackingId}")) { Outlook.Folder? inboxFolder = @@ -100,7 +101,7 @@ namespace OutlookCaseHelper Outlook.Folder casesFolder = GetOrCreateFolder(inboxFolder, "Cases"); Outlook.Folder activeFolder = GetOrCreateFolder(casesFolder, "Active"); - Outlook.Folder trackingFolder = GetOrCreateFolder(activeFolder, trackingId); + Outlook.Folder trackingFolder = GetOrCreateFolder(activeFolder, folderName); mail.Move(trackingFolder); break; @@ -110,6 +111,42 @@ namespace OutlookCaseHelper catch { } } + private string ExtractTrackingId(string folderName) + { + var match = Regex.Match(folderName, @"^(\d+)"); + return match.Success ? match.Groups[1].Value : folderName; + } + + public string? FindRuleByTrackingId(string trackingId) + { + foreach (var rule in activeRules) + { + if (ExtractTrackingId(rule) == trackingId) + return rule; + } + return null; + } + + // Verifica se existe pasta em Closed com este trackingId + public bool ExistsInClosed(string trackingId) + { + try + { + Outlook.Folder? inboxFolder = + outlookNamespace.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox) as Outlook.Folder; + if (inboxFolder == null) return false; + + Outlook.Folder? casesFolder = GetFolder(inboxFolder, "Cases"); + if (casesFolder == null) return false; + + Outlook.Folder? closedFolder = GetFolder(casesFolder, "Closed"); + if (closedFolder == null) return false; + + return GetFolderStartingWith(closedFolder, trackingId) != null; + } + catch { return false; } + } + private void LoadRules() { try @@ -159,7 +196,8 @@ namespace OutlookCaseHelper catch { return null; } } - public bool CreateFolderAndMoveEmails(string trackingId) + // Reabre caso existente em Closed e move o email trigger para lá + public bool ReopenFromClosed(string trackingId, Outlook.MailItem? triggerEmail = null) { try { @@ -169,17 +207,48 @@ namespace OutlookCaseHelper Outlook.Folder casesFolder = GetOrCreateFolder(inboxFolder, "Cases"); Outlook.Folder activeFolder = GetOrCreateFolder(casesFolder, "Active"); - Outlook.Folder trackingFolder = GetOrCreateFolder(activeFolder, trackingId); + Outlook.Folder? closedFolder = GetFolder(casesFolder, "Closed"); + if (closedFolder == null) return false; + + Outlook.Folder? closedTracking = GetFolderStartingWith(closedFolder, trackingId); + if (closedTracking == null) return false; + + string existingName = closedTracking.Name; + closedTracking.MoveTo(activeFolder); + + // Move o email que originou a reabertura para a pasta + if (triggerEmail != null) + { + Outlook.Folder? movedFolder = GetFolder(activeFolder, existingName); + if (movedFolder != null) + triggerEmail.Move(movedFolder); + } + + activeRules.Add(existingName); + SaveRules(); + return true; + } + catch { return false; } + } + + public bool CreateFolderAndMoveEmails(string trackingId, string folderName) + { + try + { + Outlook.Folder? inboxFolder = + outlookNamespace.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox) as Outlook.Folder; + if (inboxFolder == null) return false; + + Outlook.Folder casesFolder = GetOrCreateFolder(inboxFolder, "Cases"); + Outlook.Folder activeFolder = GetOrCreateFolder(casesFolder, "Active"); + Outlook.Folder trackingFolder = GetOrCreateFolder(activeFolder, folderName); - // Usa Restrict para mover apenas emails com o TrackingID string filter = $"@SQL=\"urn:schemas:httpmail:subject\" LIKE '%TrackingID#{trackingId}%'"; + Outlook.Items restricted = inboxFolder.Items.Restrict(filter); var toMove = new List(); foreach (object item in restricted) - { - if (item is Outlook.MailItem mail) - toMove.Add(mail); - } + if (item is Outlook.MailItem mail) toMove.Add(mail); foreach (var mail in toMove) mail.Move(trackingFolder); @@ -190,15 +259,12 @@ namespace OutlookCaseHelper Outlook.Items restrictedSent = sentFolder.Items.Restrict(filter); var toMoveSent = new List(); foreach (object item in restrictedSent) - { - if (item is Outlook.MailItem mail) - toMoveSent.Add(mail); - } + if (item is Outlook.MailItem mail) toMoveSent.Add(mail); foreach (var mail in toMoveSent) mail.Move(trackingFolder); } - activeRules.Add(trackingId); + activeRules.Add(folderName); SaveRules(); return true; @@ -210,7 +276,7 @@ namespace OutlookCaseHelper } } - public bool RemoveRuleAndMoveToClosed(string trackingId) + public bool RemoveRuleAndMoveToClosed(string folderName) { try { @@ -224,13 +290,13 @@ namespace OutlookCaseHelper Outlook.Folder? activeFolder = GetFolder(casesFolder, "Active"); if (activeFolder == null) return false; - Outlook.Folder? trackingFolder = GetFolder(activeFolder, trackingId); + Outlook.Folder? trackingFolder = GetFolder(activeFolder, folderName); if (trackingFolder == null) return false; Outlook.Folder closedFolder = GetOrCreateFolder(casesFolder, "Closed"); trackingFolder.MoveTo(closedFolder); - activeRules.Remove(trackingId); + activeRules.Remove(folderName); SaveRules(); return true; @@ -238,29 +304,47 @@ namespace OutlookCaseHelper catch { return false; } } - public void ProcessActiveRules() - { - // Mantido para compatibilidade mas os eventos tratam tudo em tempo real - } + public void ProcessActiveRules() { } private Outlook.Folder GetOrCreateFolder(Outlook.Folder parent, string name) { foreach (Outlook.Folder folder in parent.Folders) - { - if (folder.Name == name) - return folder; - } + if (folder.Name == name) return folder; return (Outlook.Folder)parent.Folders.Add(name); } private Outlook.Folder? GetFolder(Outlook.Folder parent, string name) + { + foreach (Outlook.Folder folder in parent.Folders) + if (folder.Name == name) return folder; + return null; + } + + private Outlook.Folder? GetFolderStartingWith(Outlook.Folder parent, string trackingId) { foreach (Outlook.Folder folder in parent.Folders) { - if (folder.Name == name) + string name = folder.Name; + if (name == trackingId || + name.StartsWith(trackingId + " ") || + name.StartsWith(trackingId + "|")) return folder; } return null; } + + // Expõe o email selecionado para ser usado no reopen + public Outlook.MailItem? GetSelectedEmail() + { + try + { + Outlook.Explorer activeExplorer = outlookApp.ActiveExplorer(); + if (activeExplorer == null) return null; + Outlook.Selection selection = activeExplorer.Selection; + if (selection.Count == 0) return null; + return selection[1] as Outlook.MailItem; + } + catch { return null; } + } } } \ No newline at end of file diff --git a/OutlookCaseHelper/OutlookHelper.resx b/OutlookCaseHelper/OutlookHelper.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/OutlookCaseHelper/OutlookHelper.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file