diff --git a/SoftwareInstallation/SofrwareInstallationContracts/BindingModels/MessageInfoBindingModel.cs b/SoftwareInstallation/SofrwareInstallationContracts/BindingModels/MessageInfoBindingModel.cs index e4259c4..ad14e2e 100644 --- a/SoftwareInstallation/SofrwareInstallationContracts/BindingModels/MessageInfoBindingModel.cs +++ b/SoftwareInstallation/SofrwareInstallationContracts/BindingModels/MessageInfoBindingModel.cs @@ -15,5 +15,7 @@ namespace SofrwareInstallationContracts.BindingModels public string Subject { get; set; } = string.Empty; public string Body { get; set; } = string.Empty; public DateTime DateDelivery { get; set; } + public bool HasRead { get; set; } + public string? Reply { get; set; } } } diff --git a/SoftwareInstallation/SofrwareInstallationContracts/BusinessLogicsContracts/IMessageInfoLogic.cs b/SoftwareInstallation/SofrwareInstallationContracts/BusinessLogicsContracts/IMessageInfoLogic.cs index f9155c7..26bfa29 100644 --- a/SoftwareInstallation/SofrwareInstallationContracts/BusinessLogicsContracts/IMessageInfoLogic.cs +++ b/SoftwareInstallation/SofrwareInstallationContracts/BusinessLogicsContracts/IMessageInfoLogic.cs @@ -9,5 +9,9 @@ namespace SofrwareInstallationContracts.BusinessLogicsContracts List? ReadList(MessageInfoSearchModel? model); bool Create(MessageInfoBindingModel model); + + bool Update(MessageInfoBindingModel model); + + MessageInfoViewModel? ReadElement(MessageInfoSearchModel model); } } diff --git a/SoftwareInstallation/SofrwareInstallationContracts/SearchModels/MessageInfoSearchModel.cs b/SoftwareInstallation/SofrwareInstallationContracts/SearchModels/MessageInfoSearchModel.cs index e7d3396..ce9510a 100644 --- a/SoftwareInstallation/SofrwareInstallationContracts/SearchModels/MessageInfoSearchModel.cs +++ b/SoftwareInstallation/SofrwareInstallationContracts/SearchModels/MessageInfoSearchModel.cs @@ -4,5 +4,7 @@ { public int? ClientId { get; set; } public string? MessageId { get; set; } + public int? Page { get; set; } + public int? PageSize { get; set; } } } diff --git a/SoftwareInstallation/SofrwareInstallationContracts/StoragesContracts/IMessageInfoStorage.cs b/SoftwareInstallation/SofrwareInstallationContracts/StoragesContracts/IMessageInfoStorage.cs index 9d9de85..e9eb4df 100644 --- a/SoftwareInstallation/SofrwareInstallationContracts/StoragesContracts/IMessageInfoStorage.cs +++ b/SoftwareInstallation/SofrwareInstallationContracts/StoragesContracts/IMessageInfoStorage.cs @@ -13,5 +13,7 @@ namespace SofrwareInstallationContracts.StoragesContracts MessageInfoViewModel? GetElement(MessageInfoSearchModel model); MessageInfoViewModel? Insert(MessageInfoBindingModel model); + + MessageInfoViewModel? Update(MessageInfoBindingModel model); } } diff --git a/SoftwareInstallation/SofrwareInstallationContracts/ViewModels/MessageInfoViewModel.cs b/SoftwareInstallation/SofrwareInstallationContracts/ViewModels/MessageInfoViewModel.cs index 464ecc4..a8a4e5d 100644 --- a/SoftwareInstallation/SofrwareInstallationContracts/ViewModels/MessageInfoViewModel.cs +++ b/SoftwareInstallation/SofrwareInstallationContracts/ViewModels/MessageInfoViewModel.cs @@ -20,5 +20,11 @@ namespace SofrwareInstallationContracts.ViewModels [DisplayName("Содержание")] public string Body { get; set; } = string.Empty; + + [DisplayName("Прочитано")] + public bool HasRead { get; set; } + + [DisplayName("Ответ")] + public string? Reply { get; set; } } } diff --git a/SoftwareInstallation/SoftwareInstallation/FormMails.Designer.cs b/SoftwareInstallation/SoftwareInstallation/FormMails.Designer.cs index 30f2ded..fb6902b 100644 --- a/SoftwareInstallation/SoftwareInstallation/FormMails.Designer.cs +++ b/SoftwareInstallation/SoftwareInstallation/FormMails.Designer.cs @@ -29,34 +29,74 @@ private void InitializeComponent() { this.DataGridView = new System.Windows.Forms.DataGridView(); + this.PrevPageButton = new System.Windows.Forms.Button(); + this.NextPageButton = new System.Windows.Forms.Button(); + this.labelInfoPages = new System.Windows.Forms.Label(); ((System.ComponentModel.ISupportInitialize)(this.DataGridView)).BeginInit(); this.SuspendLayout(); // // DataGridView // this.DataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; - this.DataGridView.Location = new System.Drawing.Point(3, 3); + this.DataGridView.Location = new System.Drawing.Point(57, 3); this.DataGridView.Name = "DataGridView"; this.DataGridView.RowTemplate.Height = 25; - this.DataGridView.Size = new System.Drawing.Size(794, 445); + this.DataGridView.Size = new System.Drawing.Size(687, 411); this.DataGridView.TabIndex = 0; + this.DataGridView.RowHeaderMouseClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.DataGridView_RowHeaderMouseClick); + // + // PrevPageButton + // + this.PrevPageButton.Location = new System.Drawing.Point(12, 158); + this.PrevPageButton.Name = "PrevPageButton"; + this.PrevPageButton.Size = new System.Drawing.Size(38, 69); + this.PrevPageButton.TabIndex = 1; + this.PrevPageButton.Text = "<="; + this.PrevPageButton.UseVisualStyleBackColor = true; + this.PrevPageButton.Click += new System.EventHandler(this.PrevPageButton_Click); + // + // NextPageButton + // + this.NextPageButton.Location = new System.Drawing.Point(750, 159); + this.NextPageButton.Name = "NextPageButton"; + this.NextPageButton.Size = new System.Drawing.Size(38, 68); + this.NextPageButton.TabIndex = 2; + this.NextPageButton.Text = "=>"; + this.NextPageButton.UseVisualStyleBackColor = true; + this.NextPageButton.Click += new System.EventHandler(this.NextPageButton_Click); + // + // labelInfoPages + // + this.labelInfoPages.AutoSize = true; + this.labelInfoPages.Location = new System.Drawing.Point(387, 426); + this.labelInfoPages.Name = "labelInfoPages"; + this.labelInfoPages.Size = new System.Drawing.Size(75, 15); + this.labelInfoPages.TabIndex = 3; + this.labelInfoPages.Text = "{0} страница"; // // FormMails // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(800, 450); + this.Controls.Add(this.labelInfoPages); + this.Controls.Add(this.NextPageButton); + this.Controls.Add(this.PrevPageButton); this.Controls.Add(this.DataGridView); this.Name = "FormMails"; this.Text = "Эл. письма"; this.Load += new System.EventHandler(this.FormMails_Load); ((System.ComponentModel.ISupportInitialize)(this.DataGridView)).EndInit(); this.ResumeLayout(false); + this.PerformLayout(); } #endregion private DataGridView DataGridView; + private Button PrevPageButton; + private Button NextPageButton; + private Label labelInfoPages; } } \ No newline at end of file diff --git a/SoftwareInstallation/SoftwareInstallation/FormMails.cs b/SoftwareInstallation/SoftwareInstallation/FormMails.cs index ebfb22b..153d2ba 100644 --- a/SoftwareInstallation/SoftwareInstallation/FormMails.cs +++ b/SoftwareInstallation/SoftwareInstallation/FormMails.cs @@ -1,6 +1,7 @@ using SofrwareInstallationContracts.BusinessLogicsContracts; using Microsoft.Extensions.Logging; using System.Windows.Forms; +using SofrwareInstallationContracts.ViewModels; namespace SoftwareInstallationView { @@ -8,20 +9,27 @@ namespace SoftwareInstallationView { private readonly ILogger _logger; private readonly IMessageInfoLogic _logic; + private int currentPage = 1; + public int pageSize = 5; public FormMails(ILogger logger, IMessageInfoLogic logic) { InitializeComponent(); _logger = logger; _logic = logic; + PrevPageButton.Enabled = false; LoadData(); } - private void LoadData() + private bool LoadData() { try { - var list = _logic.ReadList(null); + var list = _logic.ReadList(new() + { + Page = currentPage, + PageSize = pageSize, + }); if (list != null) { DataGridView.DataSource = list; @@ -30,12 +38,15 @@ namespace SoftwareInstallationView DataGridView.Columns["Body"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; } _logger.LogInformation("Загрузка писем"); + labelInfoPages.Text = $"{currentPage} страница"; + return true; } catch (Exception ex) { _logger.LogError(ex, "Ошибка загрузки писем"); MessageBox.Show(ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error); + return false; } } @@ -43,5 +54,50 @@ namespace SoftwareInstallationView { LoadData(); } + + private void PrevPageButton_Click(object sender, EventArgs e) + { + if (currentPage == 1) + { + _logger.LogWarning("Неккоректный номер страницы {page}", currentPage - 1); + return; + } + currentPage--; + if (LoadData()) + { + NextPageButton.Enabled = true; + if (currentPage == 1) + { + PrevPageButton.Enabled = false; + } + } + } + + private void NextPageButton_Click(object sender, EventArgs e) + { + currentPage++; + if (!LoadData() || ((List)DataGridView.DataSource).Count == 0) + { + _logger.LogWarning("Out of range messages"); + currentPage--; + LoadData(); + NextPageButton.Enabled = false; + } + else + { + PrevPageButton.Enabled = true; + } + } + + private void DataGridView_RowHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e) + { + var service = Program.ServiceProvider?.GetService(typeof(FormReplyMail)); + if (service is FormReplyMail form) + { + form.MessageId = (string)DataGridView.Rows[e.RowIndex].Cells["MessageId"].Value; + form.ShowDialog(); + LoadData(); + } + } } } diff --git a/SoftwareInstallation/SoftwareInstallation/FormReplyMail.Designer.cs b/SoftwareInstallation/SoftwareInstallation/FormReplyMail.Designer.cs new file mode 100644 index 0000000..bcec740 --- /dev/null +++ b/SoftwareInstallation/SoftwareInstallation/FormReplyMail.Designer.cs @@ -0,0 +1,145 @@ +namespace SoftwareInstallationView +{ + partial class FormReplyMail + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.HeadTextBox = new System.Windows.Forms.TextBox(); + this.MailTextBox = new System.Windows.Forms.TextBox(); + this.ReplyTextBox = new System.Windows.Forms.TextBox(); + this.ReplyButton = new System.Windows.Forms.Button(); + this.ButtonCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(4, 19); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(40, 15); + this.label1.TabIndex = 0; + this.label1.Text = "Тема: "; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(4, 48); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(82, 15); + this.label2.TabIndex = 1; + this.label2.Text = "Содержание: "; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(4, 178); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(44, 15); + this.label3.TabIndex = 2; + this.label3.Text = "Ответ: "; + // + // HeadTextBox + // + this.HeadTextBox.Location = new System.Drawing.Point(92, 16); + this.HeadTextBox.Name = "HeadTextBox"; + this.HeadTextBox.ReadOnly = true; + this.HeadTextBox.Size = new System.Drawing.Size(469, 23); + this.HeadTextBox.TabIndex = 5; + // + // MailTextBox + // + this.MailTextBox.Location = new System.Drawing.Point(92, 45); + this.MailTextBox.Multiline = true; + this.MailTextBox.Name = "MailTextBox"; + this.MailTextBox.ReadOnly = true; + this.MailTextBox.Size = new System.Drawing.Size(469, 93); + this.MailTextBox.TabIndex = 6; + // + // ReplyTextBox + // + this.ReplyTextBox.Location = new System.Drawing.Point(92, 175); + this.ReplyTextBox.Multiline = true; + this.ReplyTextBox.Name = "ReplyTextBox"; + this.ReplyTextBox.Size = new System.Drawing.Size(469, 125); + this.ReplyTextBox.TabIndex = 7; + // + // ReplyButton + // + this.ReplyButton.Location = new System.Drawing.Point(351, 306); + this.ReplyButton.Name = "ReplyButton"; + this.ReplyButton.Size = new System.Drawing.Size(102, 36); + this.ReplyButton.TabIndex = 6; + this.ReplyButton.Text = "Ответить"; + this.ReplyButton.UseVisualStyleBackColor = true; + this.ReplyButton.Click += new System.EventHandler(this.ReplyButton_Click); + // + // ButtonCancel + // + this.ButtonCancel.Location = new System.Drawing.Point(459, 306); + this.ButtonCancel.Name = "ButtonCancel"; + this.ButtonCancel.Size = new System.Drawing.Size(102, 36); + this.ButtonCancel.TabIndex = 7; + this.ButtonCancel.Text = "Отмена"; + this.ButtonCancel.UseVisualStyleBackColor = true; + this.ButtonCancel.Click += new System.EventHandler(this.ButtonCancel_Click); + // + // FormReplyMail + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(573, 354); + this.Controls.Add(this.ButtonCancel); + this.Controls.Add(this.ReplyButton); + this.Controls.Add(this.ReplyTextBox); + this.Controls.Add(this.MailTextBox); + this.Controls.Add(this.HeadTextBox); + this.Controls.Add(this.label3); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Name = "FormReplyMail"; + this.Text = "Ответ"; + this.Load += new System.EventHandler(this.FormReplyMail_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label label1; + private Label label2; + private Label label3; + private TextBox HeadTextBox; + private TextBox MailTextBox; + private TextBox ReplyTextBox; + private Button ReplyButton; + private Button ButtonCancel; + } +} \ No newline at end of file diff --git a/SoftwareInstallation/SoftwareInstallation/FormReplyMail.cs b/SoftwareInstallation/SoftwareInstallation/FormReplyMail.cs new file mode 100644 index 0000000..efcecee --- /dev/null +++ b/SoftwareInstallation/SoftwareInstallation/FormReplyMail.cs @@ -0,0 +1,86 @@ +using Microsoft.Extensions.Logging; +using SofrwareInstallationContracts.BusinessLogicsContracts; +using SofrwareInstallationContracts.ViewModels; +using SoftwareInstallationBusinessLogic.MailWorker; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace SoftwareInstallationView +{ + public partial class FormReplyMail : Form + { + private readonly ILogger _logger; + private readonly AbstractMailWorker _mailWorker; + private readonly IMessageInfoLogic _logic; + private MessageInfoViewModel _message; + + public string MessageId { get; set; } = string.Empty; + + public FormReplyMail(ILogger logger, AbstractMailWorker mailWorker, IMessageInfoLogic logic) + { + InitializeComponent(); + _logger = logger; + _mailWorker = mailWorker; + _logic = logic; + } + + private void ReplyButton_Click(object sender, EventArgs e) + { + _mailWorker.MailSendAsync(new() + { + MailAddress = _message.SenderName, + Subject = _message.Subject, + Text = ReplyTextBox.Text, + }); + + _logic.Update(new() + { + MessageId = MessageId, + Reply = ReplyTextBox.Text, + HasRead = true, + }); + + MessageBox.Show("Успешно отправлено письмо", "Отправка письма", MessageBoxButtons.OK); + DialogResult = DialogResult.OK; + Close(); + } + + private void ButtonCancel_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.Cancel; + Close(); + } + + private void FormReplyMail_Load(object sender, EventArgs e) + { + try + { + _message = _logic.ReadElement(new() { MessageId = MessageId }); + + if (_message == null) + throw new ArgumentNullException("Письма с таким id не существует"); + + Text += $"для {_message.SenderName}"; + HeadTextBox.Text = _message.Subject; + MailTextBox.Text = _message.Body; + + if (_message.HasRead is false) + { + _logic.Update(new() { MessageId = MessageId, HasRead = true, Reply = _message.Reply }); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Ошибка получения собщения"); + MessageBox.Show(ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } +} diff --git a/SoftwareInstallation/SoftwareInstallation/FormReplyMail.resx b/SoftwareInstallation/SoftwareInstallation/FormReplyMail.resx new file mode 100644 index 0000000..f298a7b --- /dev/null +++ b/SoftwareInstallation/SoftwareInstallation/FormReplyMail.resx @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 diff --git a/SoftwareInstallation/SoftwareInstallation/Program.cs b/SoftwareInstallation/SoftwareInstallation/Program.cs index b2ff25e..d9de629 100644 --- a/SoftwareInstallation/SoftwareInstallation/Program.cs +++ b/SoftwareInstallation/SoftwareInstallation/Program.cs @@ -91,6 +91,7 @@ namespace SoftwareInstallationView services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/SoftwareInstallation/SoftwareInstallationBusinessLogic/BusinessLogic/MessageInfoLogic.cs b/SoftwareInstallation/SoftwareInstallationBusinessLogic/BusinessLogic/MessageInfoLogic.cs index 5bfdd58..34519e0 100644 --- a/SoftwareInstallation/SoftwareInstallationBusinessLogic/BusinessLogic/MessageInfoLogic.cs +++ b/SoftwareInstallation/SoftwareInstallationBusinessLogic/BusinessLogic/MessageInfoLogic.cs @@ -46,5 +46,31 @@ namespace SoftwareInstallationBusinessLogic.BusinessLogic return list; } + + public MessageInfoViewModel? ReadElement(MessageInfoSearchModel model) + { + var res = _messageStorage.GetElement(model); + + if (res == null) + { + _logger.LogWarning("Read element operation failed"); + + return null; + } + + return res; + } + + public bool Update(MessageInfoBindingModel model) + { + if (_messageStorage.Update(model) == null) + { + _logger.LogWarning("Update operation failed"); + + return false; + } + + return true; + } } } diff --git a/SoftwareInstallation/SoftwareInstallationBusinessLogic/BusinessLogic/OrderLogic.cs b/SoftwareInstallation/SoftwareInstallationBusinessLogic/BusinessLogic/OrderLogic.cs index c26e360..a4ec9c1 100644 --- a/SoftwareInstallation/SoftwareInstallationBusinessLogic/BusinessLogic/OrderLogic.cs +++ b/SoftwareInstallation/SoftwareInstallationBusinessLogic/BusinessLogic/OrderLogic.cs @@ -18,8 +18,7 @@ namespace SoftwareInstallationBusinessLogic.BusinessLogic private readonly IStoreLogic _storeLogic; private readonly IPackageStorage _packageStorage; - public OrderLogic(ILogger logger, IOrderStorage orderStorage, AbstractMailWorker mailWorker, IClientLogic clientLogic) - public OrderLogic(ILogger logger, IOrderStorage orderStorage, IPackageStorage packageStorage,IStoreLogic storeLogic) + public OrderLogic(ILogger logger, IOrderStorage orderStorage, IPackageStorage packageStorage,IStoreLogic storeLogic, AbstractMailWorker mailWorker, IClientLogic clientLogic) { _logger = logger; _orderStorage = orderStorage; @@ -100,6 +99,9 @@ namespace SoftwareInstallationBusinessLogic.BusinessLogic _logger.LogWarning("Update operation failed"); return false; } + + SendOrderMessage(result.ClientId, $"Изменен статус заказа #{result.Id}", $"Заказ #{model.Id} изменен статус на {result.Status}"); + return true; } @@ -182,5 +184,24 @@ namespace SoftwareInstallationBusinessLogic.BusinessLogic return element; } + + private bool SendOrderMessage(int clientId, string subject, string text) + { + var client = _clientLogic.ReadElement(new() { Id = clientId }); + + if (client == null) + { + return false; + } + + _mailWorker.MailSendAsync(new() + { + MailAddress = client.Email, + Subject = subject, + Text = text + }); + + return true; + } } } diff --git a/SoftwareInstallation/SoftwareInstallationClientApp/APIClient.cs b/SoftwareInstallation/SoftwareInstallationClientApp/APIClient.cs index bff558d..15f1eba 100644 --- a/SoftwareInstallation/SoftwareInstallationClientApp/APIClient.cs +++ b/SoftwareInstallation/SoftwareInstallationClientApp/APIClient.cs @@ -8,7 +8,7 @@ namespace SoftwareInstallationClientApp public class APIClient { private static readonly HttpClient _client = new(); - + public static int CurrentPage { get; set; } = 0; public static ClientViewModel? Client { get; set; } = null; public static void Connect(IConfiguration configuration) diff --git a/SoftwareInstallation/SoftwareInstallationClientApp/Controllers/HomeController.cs b/SoftwareInstallation/SoftwareInstallationClientApp/Controllers/HomeController.cs index 0dde025..499d1b7 100644 --- a/SoftwareInstallation/SoftwareInstallationClientApp/Controllers/HomeController.cs +++ b/SoftwareInstallation/SoftwareInstallationClientApp/Controllers/HomeController.cs @@ -3,6 +3,9 @@ using SoftwareInstallationClientApp.Models; using SofrwareInstallationContracts.BindingModels; using SofrwareInstallationContracts.ViewModels; using System.Diagnostics; +using System.Text; +using SofrwareInstallationContracts.SearchModels; +using Microsoft.AspNetCore.Mvc.Rendering; namespace SoftwareInstallationClientApp.Controllers { @@ -151,7 +154,45 @@ namespace SoftwareInstallationClientApp.Controllers { return Redirect("~/Home/Enter"); } - return View(APIClient.GetRequest>($"api/client/getmessages?clientId={APIClient.Client.Id}")); + return View(); + } + + [HttpGet] + public Tuple? SwitchPage(bool isNext) + { + if (isNext) + { + APIClient.CurrentPage++; + } + else + { + if (APIClient.CurrentPage == 1) + { + return null; + } + APIClient.CurrentPage--; + } + + var res = APIClient.GetRequest>($"api/client/getmessages?clientId={APIClient.Client!.Id}&page={APIClient.CurrentPage}"); + + if (isNext && (res == null || res.Count == 0)) + { + APIClient.CurrentPage--; + return Tuple.Create(null, null, APIClient.CurrentPage != 1, false); + } + + StringBuilder htmlTable = new(); + foreach (var mail in res) + { + htmlTable.Append("" + + $"{mail.DateDelivery}" + + $"{mail.Subject}" + + $"{mail.Body}" + + "" + (mail.HasRead ? "Прочитано" : "Непрочитано") + "" + + $"{mail.Reply}" + + ""); + } + return Tuple.Create(htmlTable.ToString(), APIClient.CurrentPage.ToString(), APIClient.CurrentPage != 1, true); } } } \ No newline at end of file diff --git a/SoftwareInstallation/SoftwareInstallationClientApp/Views/Home/Mails.cshtml b/SoftwareInstallation/SoftwareInstallationClientApp/Views/Home/Mails.cshtml index b151645..b0087cf 100644 --- a/SoftwareInstallation/SoftwareInstallationClientApp/Views/Home/Mails.cshtml +++ b/SoftwareInstallation/SoftwareInstallationClientApp/Views/Home/Mails.cshtml @@ -1,54 +1,79 @@ -@using SofrwareInstallationContracts.ViewModels - -@model List - -@{ - ViewData["Title"] = "Mails"; +@{ + ViewData["Title"] = "Mails"; } -
-

Письма

+

Письма

-
- @{ - if (Model == null) - { -

Авторизируйтесь

- return; - } - - - - - - - - - - - @foreach (var item in Model) - { - - - - - - } - -
- Дата письма - - Тема - - Содержание -
- @Html.DisplayFor(modelItem => item.DateDelivery) - - @Html.DisplayFor(modelItem => item.Subject) - - @Html.DisplayFor(modelItem => item.Body) -
- } + + + + + + + + + + + + +
+ Дата письма + + Тема + + Содержание + + Статус + + Ответ +
+
+ + \ No newline at end of file diff --git a/SoftwareInstallation/SoftwareInstallationDataBaseImplement/Implements/MessageInfoStorage.cs b/SoftwareInstallation/SoftwareInstallationDataBaseImplement/Implements/MessageInfoStorage.cs index 5805cd3..b52ff4e 100644 --- a/SoftwareInstallation/SoftwareInstallationDataBaseImplement/Implements/MessageInfoStorage.cs +++ b/SoftwareInstallation/SoftwareInstallationDataBaseImplement/Implements/MessageInfoStorage.cs @@ -18,13 +18,18 @@ namespace SoftwareInstallationDataBaseImplement.Implements public List GetFilteredList(MessageInfoSearchModel model) { - if (!model.ClientId.HasValue) - return new(); using var context = new SoftwareInstallationDataBase(); - return context.Messages - .Where(x => x.ClientId == model.ClientId) - .Select(x => x.GetViewModel) - .ToList(); + + var res = context.Messages + .Where(x => !model.ClientId.HasValue || x.ClientId == model.ClientId) + .Select(x => x.GetViewModel); + + if (!(model.Page.HasValue && model.PageSize.HasValue)) + { + return res.ToList(); + } + + return res.Skip((model.Page.Value - 1) * model.PageSize.Value).Take(model.PageSize.Value).ToList(); } public List GetFullList() @@ -37,15 +42,31 @@ namespace SoftwareInstallationDataBaseImplement.Implements public MessageInfoViewModel? Insert(MessageInfoBindingModel model) { + using var context = new SoftwareInstallationDataBase(); var newMessage = Message.Create(model); - if (newMessage == null) + + if (newMessage == null || context.Messages.Any(x => x.MessageId.Equals(model.MessageId))) { return null; } - using var context = new SoftwareInstallationDataBase(); + context.Messages.Add(newMessage); context.SaveChanges(); return newMessage.GetViewModel; } + + public MessageInfoViewModel? Update(MessageInfoBindingModel model) + { + using var context = new SoftwareInstallationDataBase(); + var res = context.Messages.FirstOrDefault(x => x.MessageId.Equals(model.MessageId)); + + if (res != null) + { + res.Update(model); + context.SaveChanges(); + } + + return res?.GetViewModel; + } } } diff --git a/SoftwareInstallation/SoftwareInstallationDataBaseImplement/Migrations/SoftwareInstallationDataBaseModelSnapshot.cs b/SoftwareInstallation/SoftwareInstallationDataBaseImplement/Migrations/SoftwareInstallationDataBaseModelSnapshot.cs deleted file mode 100644 index ae1125d..0000000 --- a/SoftwareInstallation/SoftwareInstallationDataBaseImplement/Migrations/SoftwareInstallationDataBaseModelSnapshot.cs +++ /dev/null @@ -1,337 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SoftwareInstallationDataBaseImplement; - -#nullable disable - -namespace SoftwareInstallationDataBaseImplement.Migrations -{ - [DbContext(typeof(SoftwareInstallationDataBase))] - partial class SoftwareInstallationDataBaseModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.3") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("SoftwareInstallationDataBaseImplement.Models.Client", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClientFIO") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Password") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Clients"); - }); - - modelBuilder.Entity("SoftwareInstallationDataBaseImplement.Models.Component", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ComponentName") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Cost") - .HasColumnType("float"); - - b.HasKey("Id"); - - b.ToTable("Components"); - }); - - modelBuilder.Entity("SoftwareInstallationDataBaseImplement.Models.Implementer", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ImplementerFIO") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Password") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Qualification") - .HasColumnType("int"); - - b.Property("WorkExperience") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.ToTable("Implementers"); - }); - - modelBuilder.Entity("SoftwareInstallationDataBaseImplement.Models.Order", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("Count") - .HasColumnType("int"); - - b.Property("DateCreate") - .HasColumnType("datetime2"); - - b.Property("DateImplement") - .HasColumnType("datetime2"); - - b.Property("ImplementerId") - .HasColumnType("int"); - - b.Property("PackageId") - .HasColumnType("int"); - - b.Property("PackageName") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Status") - .HasColumnType("int"); - - b.Property("Sum") - .HasColumnType("float"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("ImplementerId"); - - b.HasIndex("PackageId"); - - b.ToTable("Orders"); - }); - - modelBuilder.Entity("SoftwareInstallationDataBaseImplement.Models.Package", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("PackageName") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Price") - .HasColumnType("float"); - - b.HasKey("Id"); - - b.ToTable("Packages"); - }); - - modelBuilder.Entity("SoftwareInstallationDataBaseImplement.Models.PackageComponent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ComponentId") - .HasColumnType("int"); - - b.Property("Count") - .HasColumnType("int"); - - b.Property("PackageId") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.HasIndex("ComponentId"); - - b.HasIndex("PackageId"); - - b.ToTable("PackageComponents"); - }); - - modelBuilder.Entity("SoftwareInstallationDataBaseImplement.Models.Store", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("OpeningDate") - .HasColumnType("datetime2"); - - b.Property("PackageMaxCount") - .HasColumnType("int"); - - b.Property("StoreAdress") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("StoreName") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Stores"); - }); - - modelBuilder.Entity("SoftwareInstallationDataBaseImplement.Models.StorePackage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("Count") - .HasColumnType("int"); - - b.Property("PackageId") - .HasColumnType("int"); - - b.Property("StoreId") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.HasIndex("PackageId"); - - b.HasIndex("StoreId"); - - b.ToTable("StorePackages"); - }); - - modelBuilder.Entity("SoftwareInstallationDataBaseImplement.Models.Order", b => - { - b.HasOne("SoftwareInstallationDataBaseImplement.Models.Client", "Client") - .WithMany("Orders") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SoftwareInstallationDataBaseImplement.Models.Implementer", "Implementer") - .WithMany("Orders") - .HasForeignKey("ImplementerId"); - - b.HasOne("SoftwareInstallationDataBaseImplement.Models.Package", "Package") - .WithMany("Orders") - .HasForeignKey("PackageId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - - b.Navigation("Implementer"); - - b.Navigation("Package"); - }); - - modelBuilder.Entity("SoftwareInstallationDataBaseImplement.Models.PackageComponent", b => - { - b.HasOne("SoftwareInstallationDataBaseImplement.Models.Component", "Component") - .WithMany("PackageComponents") - .HasForeignKey("ComponentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SoftwareInstallationDataBaseImplement.Models.Package", "Package") - .WithMany("Components") - .HasForeignKey("PackageId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Component"); - - b.Navigation("Package"); - }); - - modelBuilder.Entity("SoftwareInstallationDataBaseImplement.Models.StorePackage", b => - { - b.HasOne("SoftwareInstallationDataBaseImplement.Models.Package", "Package") - .WithMany("StorePackages") - .HasForeignKey("PackageId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SoftwareInstallationDataBaseImplement.Models.Store", "Store") - .WithMany("Packages") - .HasForeignKey("StoreId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Package"); - - b.Navigation("Store"); - }); - - modelBuilder.Entity("SoftwareInstallationDataBaseImplement.Models.Client", b => - { - b.Navigation("Orders"); - }); - - modelBuilder.Entity("SoftwareInstallationDataBaseImplement.Models.Component", b => - { - b.Navigation("PackageComponents"); - }); - - modelBuilder.Entity("SoftwareInstallationDataBaseImplement.Models.Implementer", b => - { - b.Navigation("Orders"); - }); - - modelBuilder.Entity("SoftwareInstallationDataBaseImplement.Models.Package", b => - { - b.Navigation("Components"); - - b.Navigation("Orders"); - - b.Navigation("StorePackages"); - }); - - modelBuilder.Entity("SoftwareInstallationDataBaseImplement.Models.Store", b => - { - b.Navigation("Packages"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/SoftwareInstallation/SoftwareInstallationDataBaseImplement/Models/Message.cs b/SoftwareInstallation/SoftwareInstallationDataBaseImplement/Models/Message.cs index 2f1b063..b389c26 100644 --- a/SoftwareInstallation/SoftwareInstallationDataBaseImplement/Models/Message.cs +++ b/SoftwareInstallation/SoftwareInstallationDataBaseImplement/Models/Message.cs @@ -18,6 +18,10 @@ namespace SoftwareInstallationDataBaseImplement.Models public string Subject { get; private set; } = string.Empty; [Required] public string Body { get; private set; } = string.Empty; + [Required] + public bool HasRead { get; private set; } + [Required] + public string? Reply { get; private set; } public virtual Client Client { get; set; } @@ -30,6 +34,8 @@ namespace SoftwareInstallationDataBaseImplement.Models return new() { Body = model.Body, + Reply = model.Reply, + HasRead = model.HasRead, Subject = model.Subject, ClientId = model.ClientId, MessageId = model.MessageId, @@ -38,9 +44,21 @@ namespace SoftwareInstallationDataBaseImplement.Models }; } + public void Update(MessageInfoBindingModel model) + { + if (model == null) + { + return; + } + Reply = model.Reply; + HasRead = model.HasRead; + } + public MessageInfoViewModel GetViewModel => new() { Body = Body, + Reply = Reply, + HasRead = HasRead, Subject = Subject, ClientId = ClientId, MessageId = MessageId, diff --git a/SoftwareInstallation/SoftwareInstallationDataModels/Models/IMessageInfoModel.cs b/SoftwareInstallation/SoftwareInstallationDataModels/Models/IMessageInfoModel.cs index a27b3f7..27dcdbb 100644 --- a/SoftwareInstallation/SoftwareInstallationDataModels/Models/IMessageInfoModel.cs +++ b/SoftwareInstallation/SoftwareInstallationDataModels/Models/IMessageInfoModel.cs @@ -8,5 +8,7 @@ DateTime DateDelivery { get; } string Subject { get; } string Body { get; } + public bool HasRead { get; } + public string? Reply { get; } } } diff --git a/SoftwareInstallation/SoftwareInstallationFileImplement/DataFileSingleton.cs b/SoftwareInstallation/SoftwareInstallationFileImplement/DataFileSingleton.cs index 1331af0..7163b91 100644 --- a/SoftwareInstallation/SoftwareInstallationFileImplement/DataFileSingleton.cs +++ b/SoftwareInstallation/SoftwareInstallationFileImplement/DataFileSingleton.cs @@ -38,8 +38,6 @@ namespace SoftwareInstallationFileImplement public void SaveClients() => SaveData(Clients, ClientFileName, "Clients", x => x.GetXElement); public void SaveImplementers() => SaveData(Implementers, ImplementerFileName, "Implementers", x => x.GetXElement); public void SaveMessages() => SaveData(Messages, MessageFileName, "Messages", x => x.GetXElement); - public void SaveClients() => SaveData(Clients, OrderFileName, "Clients", x => x.GetXElement); - public void SaveImplementers() => SaveData(Implementers, OrderFileName, "Implementers", x => x.GetXElement); public void SaveStores() => SaveData(Stores, StoreFileName, "Stores", x => x.GetXElement); private DataFileSingleton() diff --git a/SoftwareInstallation/SoftwareInstallationFileImplement/Implements/MessageInfoStorage.cs b/SoftwareInstallation/SoftwareInstallationFileImplement/Implements/MessageInfoStorage.cs index 7d4eac0..5e3fda2 100644 --- a/SoftwareInstallation/SoftwareInstallationFileImplement/Implements/MessageInfoStorage.cs +++ b/SoftwareInstallation/SoftwareInstallationFileImplement/Implements/MessageInfoStorage.cs @@ -22,12 +22,14 @@ namespace SoftwareInstallationFileImplement.Implements public List GetFilteredList(MessageInfoSearchModel model) { - if (!model.ClientId.HasValue) - return new(); - return source.Messages - .Where(x => x.ClientId == model.ClientId) - .Select(x => x.GetViewModel) - .ToList(); + var res = source.Messages + .Where(x => !model.ClientId.HasValue || x.ClientId == model.ClientId) + .Select(x => x.GetViewModel); + if (!(model.Page.HasValue && model.PageSize.HasValue)) + { + return res.ToList(); + } + return res.Skip((model.Page.Value - 1) * model.PageSize.Value).Take(model.PageSize.Value).ToList(); } public List GetFullList() @@ -48,5 +50,16 @@ namespace SoftwareInstallationFileImplement.Implements source.SaveMessages(); return newMessage.GetViewModel; } + + public MessageInfoViewModel? Update(MessageInfoBindingModel model) + { + var res = source.Messages.FirstOrDefault(x => x.MessageId.Equals(model.MessageId)); + if (res != null) + { + res.Update(model); + source.SaveMessages(); + } + return res?.GetViewModel; + } } } diff --git a/SoftwareInstallation/SoftwareInstallationFileImplement/Models/Message.cs b/SoftwareInstallation/SoftwareInstallationFileImplement/Models/Message.cs index cb19a04..7431ba0 100644 --- a/SoftwareInstallation/SoftwareInstallationFileImplement/Models/Message.cs +++ b/SoftwareInstallation/SoftwareInstallationFileImplement/Models/Message.cs @@ -19,6 +19,10 @@ namespace SoftwareInstallationFileImplement.Models public string Body { get; private set; } = string.Empty; + public bool HasRead { get; private set; } + + public string? Reply { get; private set; } + public static Message? Create(MessageInfoBindingModel model) { if (model == null) @@ -27,6 +31,8 @@ namespace SoftwareInstallationFileImplement.Models } return new() { + Reply = model.Reply, + HasRead = model.HasRead, Body = model.Body, Subject = model.Subject, DateDelivery = model.DateDelivery, @@ -44,6 +50,8 @@ namespace SoftwareInstallationFileImplement.Models } return new() { + Reply = element.Attribute("Reply")!.Value, + HasRead = Convert.ToBoolean(element.Attribute("HasRead")!.Value), Body = element.Attribute("Body")!.Value, Subject = element.Attribute("Subject")!.Value, DateDelivery = Convert.ToDateTime(element.Attribute("DateDelivery")!.Value), @@ -53,10 +61,22 @@ namespace SoftwareInstallationFileImplement.Models }; } + public void Update(MessageInfoBindingModel model) + { + if (model == null) + { + return; + } + Reply = model.Reply; + HasRead = model.HasRead; + } + public MessageInfoViewModel GetViewModel => new() { Body = Body, Subject = Subject, + Reply = Reply, + HasRead = HasRead, DateDelivery = DateDelivery, SenderName = SenderName, ClientId = ClientId, @@ -66,6 +86,8 @@ namespace SoftwareInstallationFileImplement.Models public XElement GetXElement => new("MessageInfo", new XAttribute("Subject", Subject), new XAttribute("Body", Body), + new XAttribute("Reply", Reply), + new XAttribute("HasRead", HasRead), new XAttribute("ClientId", ClientId), new XAttribute("MessageId", MessageId), new XAttribute("SenderName", SenderName), diff --git a/SoftwareInstallation/SoftwareInstallationListImplement/Implements/MessageInfoStorage.cs b/SoftwareInstallation/SoftwareInstallationListImplement/Implements/MessageInfoStorage.cs index 2fbee70..8e05295 100644 --- a/SoftwareInstallation/SoftwareInstallationListImplement/Implements/MessageInfoStorage.cs +++ b/SoftwareInstallation/SoftwareInstallationListImplement/Implements/MessageInfoStorage.cs @@ -41,7 +41,21 @@ namespace SoftwareInstallationListImplement.Implements result.Add(item.GetViewModel); } } - return result; + + if (!(model.Page.HasValue && model.PageSize.HasValue)) + { + return result; + } + if (model.Page * model.PageSize >= result.Count) + { + return null; + } + List filteredResult = new(); + for (var i = (model.Page.Value - 1) * model.PageSize.Value; i < model.Page.Value * model.PageSize.Value; i++) + { + filteredResult.Add(result[i]); + } + return filteredResult; } public List GetFullList() @@ -64,5 +78,18 @@ namespace SoftwareInstallationListImplement.Implements _source.Messages.Add(newMessage); return newMessage.GetViewModel; } + + public MessageInfoViewModel? Update(MessageInfoBindingModel model) + { + foreach (var message in _source.Messages) + { + if (message.MessageId.Equals(model.MessageId)) + { + message.Update(model); + return message.GetViewModel; + } + } + return null; + } } } diff --git a/SoftwareInstallation/SoftwareInstallationListImplement/Models/Message.cs b/SoftwareInstallation/SoftwareInstallationListImplement/Models/Message.cs index 47cfc9e..a0d1068 100644 --- a/SoftwareInstallation/SoftwareInstallationListImplement/Models/Message.cs +++ b/SoftwareInstallation/SoftwareInstallationListImplement/Models/Message.cs @@ -23,6 +23,10 @@ namespace SoftwareInstallationListImplement.Models public string Body { get; private set; } = string.Empty; + public bool HasRead { get; private set; } + + public string? Reply { get; private set; } + public static Message? Create(MessageInfoBindingModel model) { if (model == null) @@ -31,6 +35,8 @@ namespace SoftwareInstallationListImplement.Models } return new() { + Reply = model.Reply, + HasRead = model.HasRead, Body = model.Body, Subject = model.Subject, DateDelivery = model.DateDelivery, @@ -40,9 +46,21 @@ namespace SoftwareInstallationListImplement.Models }; } + public void Update(MessageInfoBindingModel model) + { + if (model == null) + { + return; + } + Reply = model.Reply; + HasRead = model.HasRead; + } + public MessageInfoViewModel GetViewModel => new() { Body = Body, + Reply = Reply, + HasRead = HasRead, Subject = Subject, DateDelivery = DateDelivery, SenderName = SenderName, diff --git a/SoftwareInstallation/SoftwareInstallationRestApi/Controllers/ClientController.cs b/SoftwareInstallation/SoftwareInstallationRestApi/Controllers/ClientController.cs index a777479..b2a3209 100644 --- a/SoftwareInstallation/SoftwareInstallationRestApi/Controllers/ClientController.cs +++ b/SoftwareInstallation/SoftwareInstallationRestApi/Controllers/ClientController.cs @@ -14,6 +14,7 @@ namespace SoftwareInstallationRestApi.Controllers private readonly ILogger _logger; private readonly IClientLogic _logic; private readonly IMessageInfoLogic _mailLogic; + public int pageSize = 3; public ClientController(IClientLogic logic, ILogger logger, IMessageInfoLogic mailLogic) { @@ -22,6 +23,25 @@ namespace SoftwareInstallationRestApi.Controllers _mailLogic = mailLogic; } + [HttpGet] + public List? GetMessages(int clientId, int page) + { + try + { + return _mailLogic.ReadList(new() + { + ClientId = clientId, + Page = page, + PageSize = pageSize + }); + } + catch (Exception ex) + { + _logger.LogError(ex, "Ошибка получения писем клиента"); + throw; + } + } + [HttpGet] public ClientViewModel? Login(string login, string password) {