Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
0ea38937e0 | ||
|
4d18ba7e8b | ||
|
a54010df45 | ||
|
e8d9ca4710 | ||
|
afe63df4b2 | ||
|
07cf4a8725 | ||
|
62be76e0e6 | ||
|
13ffe5732e | ||
|
de4b7c8c07 | ||
|
b724bcb977 | ||
|
8c0c68858b | ||
|
96f8c64d3c | ||
|
4094d029ef | ||
|
de1eb8e89b | ||
|
c55649ea00 | ||
|
d70ec5993b | ||
|
390b0d1012 | ||
|
02ff5049ff | ||
|
de62f66134 | ||
|
87f2768ec5 | ||
|
b618ea277e | ||
|
b4d7679154 | ||
|
458859d995 | ||
|
b868a9f45a |
84
FurnitureAssembly/FurnitureAssembly.sln
Normal file
84
FurnitureAssembly/FurnitureAssembly.sln
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.8.34525.116
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FurnitureAssemblyContracts", "FurnitureAssemblyContracts\FurnitureAssemblyContracts.csproj", "{A5F526DF-C1CD-4FCA-87E6-BDB447FF959C}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FurnitureAssemblyDataModels", "FurnitureAssemblyDataModels\FurnitureAssemblyDataModels.csproj", "{7A1F9F2F-DDA5-4C97-8FAA-149D5372365D}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FurnitureAssemblyBusinessLogic", "FurnitureAssemblyBusinessLogic\FurnitureAssemblyBusinessLogic.csproj", "{27F73911-7647-475A-875D-C7A4046B0958}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FurnitureAssemblyListImplement", "FurnitureAssemblyListImplement\FurnitureAssemblyListImplement.csproj", "{332092CD-AA0D-475A-88A2-2E887E29E56A}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FurnitureAssemblyView", "FurnitureAssemblyView\FurnitureAssemblyView.csproj", "{77944C5A-53FC-437B-A563-E7C09C769859}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{8DA61C13-D732-4AA0-8D0A-3569022681D2} = {8DA61C13-D732-4AA0-8D0A-3569022681D2}
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FurnitureAssemblyFileImplement", "FurnitureAssemblyFileImplement\FurnitureAssemblyFileImplement.csproj", "{8DA61C13-D732-4AA0-8D0A-3569022681D2}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{7A1F9F2F-DDA5-4C97-8FAA-149D5372365D} = {7A1F9F2F-DDA5-4C97-8FAA-149D5372365D}
|
||||||
|
{A5F526DF-C1CD-4FCA-87E6-BDB447FF959C} = {A5F526DF-C1CD-4FCA-87E6-BDB447FF959C}
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FurnitureAssemblyDatabaseImplement", "FurnitureAssemblyDatabaseImplement\FurnitureAssemblyDatabaseImplement.csproj", "{AB4E4750-5DA4-494F-A45F-B9CEE69299BD}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{7A1F9F2F-DDA5-4C97-8FAA-149D5372365D} = {7A1F9F2F-DDA5-4C97-8FAA-149D5372365D}
|
||||||
|
{A5F526DF-C1CD-4FCA-87E6-BDB447FF959C} = {A5F526DF-C1CD-4FCA-87E6-BDB447FF959C}
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FurnitureAssemblyClientApp", "FurnitureAssemblyClientApp\FurnitureAssemblyClientApp.csproj", "{0CD0AE46-B6EE-4167-BBBE-9F4D8D60CD63}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FurnitureAssemblyRestApi", "FurnitureAssemblyRestApi\FurnitureAssemblyRestApi.csproj", "{88C9CAED-9257-41BD-8582-67553AEDFCFA}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{A5F526DF-C1CD-4FCA-87E6-BDB447FF959C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{A5F526DF-C1CD-4FCA-87E6-BDB447FF959C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{A5F526DF-C1CD-4FCA-87E6-BDB447FF959C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{A5F526DF-C1CD-4FCA-87E6-BDB447FF959C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{7A1F9F2F-DDA5-4C97-8FAA-149D5372365D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{7A1F9F2F-DDA5-4C97-8FAA-149D5372365D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{7A1F9F2F-DDA5-4C97-8FAA-149D5372365D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{7A1F9F2F-DDA5-4C97-8FAA-149D5372365D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{27F73911-7647-475A-875D-C7A4046B0958}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{27F73911-7647-475A-875D-C7A4046B0958}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{27F73911-7647-475A-875D-C7A4046B0958}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{27F73911-7647-475A-875D-C7A4046B0958}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{332092CD-AA0D-475A-88A2-2E887E29E56A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{332092CD-AA0D-475A-88A2-2E887E29E56A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{332092CD-AA0D-475A-88A2-2E887E29E56A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{332092CD-AA0D-475A-88A2-2E887E29E56A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{77944C5A-53FC-437B-A563-E7C09C769859}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{77944C5A-53FC-437B-A563-E7C09C769859}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{77944C5A-53FC-437B-A563-E7C09C769859}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{77944C5A-53FC-437B-A563-E7C09C769859}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8DA61C13-D732-4AA0-8D0A-3569022681D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8DA61C13-D732-4AA0-8D0A-3569022681D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8DA61C13-D732-4AA0-8D0A-3569022681D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8DA61C13-D732-4AA0-8D0A-3569022681D2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{AB4E4750-5DA4-494F-A45F-B9CEE69299BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{AB4E4750-5DA4-494F-A45F-B9CEE69299BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{AB4E4750-5DA4-494F-A45F-B9CEE69299BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{AB4E4750-5DA4-494F-A45F-B9CEE69299BD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0CD0AE46-B6EE-4167-BBBE-9F4D8D60CD63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0CD0AE46-B6EE-4167-BBBE-9F4D8D60CD63}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0CD0AE46-B6EE-4167-BBBE-9F4D8D60CD63}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0CD0AE46-B6EE-4167-BBBE-9F4D8D60CD63}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{88C9CAED-9257-41BD-8582-67553AEDFCFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{88C9CAED-9257-41BD-8582-67553AEDFCFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{88C9CAED-9257-41BD-8582-67553AEDFCFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{88C9CAED-9257-41BD-8582-67553AEDFCFA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {27CD86DA-500B-4657-9703-3564F0C00790}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
@ -0,0 +1,134 @@
|
|||||||
|
using FurnitureAssemblyContracts.BindingModels;
|
||||||
|
using FurnitureAssemblyContracts.BusinessLogicsContracts;
|
||||||
|
using FurnitureAssemblyContracts.StoragesContracts;
|
||||||
|
using FurnitureAssemblyDataModels;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.Serialization.Json;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.BussinessLogic
|
||||||
|
{
|
||||||
|
// Класс, реализующий логику для BackUp
|
||||||
|
public class BackUpLogic : IBackUpLogic
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
private readonly IBackUpInfo _backUpInfo;
|
||||||
|
|
||||||
|
// Конструктор
|
||||||
|
public BackUpLogic(ILogger<BackUpLogic> logger, IBackUpInfo backUpInfo)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_backUpInfo = backUpInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateBackUp(BackUpSaveBindingModel model)
|
||||||
|
{
|
||||||
|
// Проверка наличия данных для бэкапа
|
||||||
|
if (_backUpInfo == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogDebug("Clear folder");
|
||||||
|
|
||||||
|
// Зачистка папки и удаление старого архива
|
||||||
|
var dirInfo = new DirectoryInfo(model.FolderName);
|
||||||
|
|
||||||
|
if (dirInfo.Exists)
|
||||||
|
{
|
||||||
|
// ЛУЧШЕ ВЫБИРАТЬ ОТДЕЛЬНО СОЗДАННУЮ ПАПКУ (Рабочий стол не использовать, поскольку с него всё удалится)
|
||||||
|
foreach (var file in dirInfo.GetFiles())
|
||||||
|
{
|
||||||
|
file.Delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogDebug("Delete archive");
|
||||||
|
|
||||||
|
string fileName = $"{model.FolderName}.zip";
|
||||||
|
|
||||||
|
if (File.Exists(fileName))
|
||||||
|
{
|
||||||
|
File.Delete(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Берём метод для сохранения
|
||||||
|
_logger.LogDebug("Get assembly");
|
||||||
|
|
||||||
|
// Получение сборки и типов
|
||||||
|
var typeIId = typeof(IId);
|
||||||
|
var assembly = typeIId.Assembly;
|
||||||
|
|
||||||
|
if (assembly == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Сборка не найдена", nameof(assembly));
|
||||||
|
}
|
||||||
|
|
||||||
|
var types = assembly.GetTypes();
|
||||||
|
var method = GetType().GetMethod("SaveToFile", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
|
||||||
|
_logger.LogDebug("Find {count} types", types.Length);
|
||||||
|
|
||||||
|
// Перебор типов и вызов метода для сохранения данных
|
||||||
|
foreach (var type in types)
|
||||||
|
{
|
||||||
|
// Проверка на то, является ли тип интерфейсом и унаследован ли он от IId
|
||||||
|
if (type.IsInterface && type.GetInterface(typeIId.Name) != null)
|
||||||
|
{
|
||||||
|
var modelType = _backUpInfo.GetTypeByModelInterface(type.Name);
|
||||||
|
|
||||||
|
if (modelType == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Не найден класс - модель для {type.Name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogDebug("Call SaveToFile method for {name} type", type.Name);
|
||||||
|
|
||||||
|
// Вызываем метод на выполнение (Вызываем метод типа MethodInfo)
|
||||||
|
method?.MakeGenericMethod(modelType).Invoke(this, new object[] { model.FolderName });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogDebug("Create zip and remove folder");
|
||||||
|
|
||||||
|
// Архивируем
|
||||||
|
ZipFile.CreateFromDirectory(model.FolderName, fileName);
|
||||||
|
|
||||||
|
// Удаляем папку
|
||||||
|
dirInfo.Delete(true);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveToFile<T>(string folderName) where T : class, new()
|
||||||
|
{
|
||||||
|
var records = _backUpInfo.GetList<T>();
|
||||||
|
|
||||||
|
if (records == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("{type} type get null list", typeof(T).Name);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Три строчки ниже - сериализация файлов в json
|
||||||
|
var jsonFormatter = new DataContractJsonSerializer(typeof(List<T>));
|
||||||
|
|
||||||
|
using var fs = new FileStream(string.Format("{0}/{1}.json", folderName, typeof(T).Name), FileMode.OpenOrCreate);
|
||||||
|
|
||||||
|
jsonFormatter.WriteObject(fs, records);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,173 @@
|
|||||||
|
using FurnitureAssemblyContracts.BindingModels;
|
||||||
|
using FurnitureAssemblyContracts.BusinessLogicsContracts;
|
||||||
|
using FurnitureAssemblyContracts.SearchModels;
|
||||||
|
using FurnitureAssemblyContracts.StoragesContracts;
|
||||||
|
using FurnitureAssemblyContracts.ViewModels;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.BussinessLogic
|
||||||
|
{
|
||||||
|
// Класс, реализующий логику для клиентов
|
||||||
|
public class ClientLogic : IClientLogic
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
private readonly IClientStorage _clientStorage;
|
||||||
|
|
||||||
|
// Конструктор
|
||||||
|
public ClientLogic(ILogger<ClientLogic> logger, IClientStorage clientStorage)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_clientStorage = clientStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ClientViewModel>? ReadList(ClientSearchModel? model)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("ReadList. Email:{Email}. Id:{Id}", model?.Email, model?.Id);
|
||||||
|
|
||||||
|
// list хранит весь список в случае, если model пришло со значением null на вход метода
|
||||||
|
var list = model == null ? _clientStorage.GetFullList() : _clientStorage.GetFilteredList(model);
|
||||||
|
|
||||||
|
if (list == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ReadList return null list");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("ReadList. Count:{Count}", list.Count);
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientViewModel? ReadElement(ClientSearchModel model)
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("ReadList. Email:{Email}. Id:{Id}", model.Email, model?.Id);
|
||||||
|
|
||||||
|
var element = _clientStorage.GetElement(model);
|
||||||
|
|
||||||
|
if (element == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ReadElement element not found");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("ReadElement find .Id:{Id}", element.Id);
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Create(ClientBindingModel model)
|
||||||
|
{
|
||||||
|
CheckModel(model);
|
||||||
|
|
||||||
|
if (_clientStorage.Insert(model) == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Insert operation failed");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Update(ClientBindingModel model)
|
||||||
|
{
|
||||||
|
CheckModel(model);
|
||||||
|
|
||||||
|
if (_clientStorage.Update(model) == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Update operation failed");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Delete(ClientBindingModel model)
|
||||||
|
{
|
||||||
|
CheckModel(model, false);
|
||||||
|
|
||||||
|
_logger.LogInformation("Delete. Id:{Id}", model.Id);
|
||||||
|
|
||||||
|
if (_clientStorage.Delete(model) == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Delete operation failed");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка входного аргумента для методов Insert, Update и Delete
|
||||||
|
private void CheckModel(ClientBindingModel model, bool withParams = true)
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
// При удалении передаём как параметр false
|
||||||
|
if (!withParams)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на наличие Фамилии Имени и Отчества
|
||||||
|
if (string.IsNullOrEmpty(model.ClientFIO))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Отсутствие ФИО в учётной записи", nameof(model.ClientFIO));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на наличие эл. почты
|
||||||
|
if (string.IsNullOrEmpty(model.Email))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Отсутствие эл. почты в учётной записи (логина)", nameof(model.Email));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на наличие пароля
|
||||||
|
if (string.IsNullOrEmpty(model.Password))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Отсутствие пароля в учётной записи", nameof(model.Password));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Regex.IsMatch(model.Email, @"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$", RegexOptions.IgnoreCase))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Некорректная почта", nameof(model.Email));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Regex.IsMatch(model.Password, @"^((\w+\d+\W+)|(\w+\W+\d+)|(\d+\w+\W+)|(\d+\W+\w+)|(\W+\w+\d+)|(\W+\d+\w+))[\w\d\W]*$", RegexOptions.IgnoreCase)
|
||||||
|
&& model.Password.Length < 10 && model.Password.Length > 50)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Необходимо придумать другой пароль", nameof(model.Password));
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Client. ClientFIO:{ClientFIO}. Email:{Email}. Id:{Id} ", model.ClientFIO, model.Email, model.Id);
|
||||||
|
|
||||||
|
// Для проверка на наличие такого же аккаунта
|
||||||
|
var element = _clientStorage.GetElement(new ClientSearchModel
|
||||||
|
{
|
||||||
|
Email = model.Email
|
||||||
|
});
|
||||||
|
|
||||||
|
// Если элемент найден и его Id не совпадает с Id переданного объекта
|
||||||
|
if (element != null && element.Id != model.Id)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Аккаунт с таким логином уже есть");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,161 @@
|
|||||||
|
using FurnitureAssemblyContracts.BindingModels;
|
||||||
|
using FurnitureAssemblyContracts.BusinessLogicsContracts;
|
||||||
|
using FurnitureAssemblyContracts.SearchModels;
|
||||||
|
using FurnitureAssemblyContracts.StoragesContracts;
|
||||||
|
using FurnitureAssemblyContracts.ViewModels;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.BussinessLogic
|
||||||
|
{
|
||||||
|
// Класс, реализующий логику для изделий
|
||||||
|
public class FurnitureLogic : IFurnitureLogic
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
private readonly IFurnitureStorage _furnitureStorage;
|
||||||
|
|
||||||
|
// Конструктор
|
||||||
|
public FurnitureLogic(ILogger<FurnitureLogic> logger, IFurnitureStorage furnitureStorage)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_furnitureStorage = furnitureStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вывод отфильтрованного списка
|
||||||
|
public List<FurnitureViewModel>? ReadList(FurnitureSearchModel? model)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("ReadList. FurnitureName: {FurnitureName}. Id:{Id}", model?.FurnitureName, model?.Id);
|
||||||
|
|
||||||
|
// list хранит весь список в случае, если model пришло со значением null на вход метода
|
||||||
|
var list = model == null ? _furnitureStorage.GetFullList() : _furnitureStorage.GetFilteredList(model);
|
||||||
|
|
||||||
|
if(list == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ReadList return null list");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("ReadList. Count:{Count}", list.Count);
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вывод конкретного изделия
|
||||||
|
public FurnitureViewModel? ReadElement(FurnitureSearchModel model)
|
||||||
|
{
|
||||||
|
if(model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("ReadElement. FurnitureName: {FurnitureName}. Id:{Id}", model.FurnitureName, model.Id);
|
||||||
|
|
||||||
|
var element = _furnitureStorage.GetElement(model);
|
||||||
|
|
||||||
|
if(element == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ReadElement element not found");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Readelement find. Id:{Id}", model.Id);
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создание изделия
|
||||||
|
public bool Create(FurnitureBindingModel model)
|
||||||
|
{
|
||||||
|
CheckModel(model);
|
||||||
|
|
||||||
|
if(_furnitureStorage.Insert(model) == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Create operation failed");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновление изделия
|
||||||
|
public bool Update(FurnitureBindingModel model)
|
||||||
|
{
|
||||||
|
CheckModel(model);
|
||||||
|
|
||||||
|
if(_furnitureStorage.Update(model) == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Update operation failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаление изделия
|
||||||
|
public bool Delete(FurnitureBindingModel model)
|
||||||
|
{
|
||||||
|
CheckModel(model, false);
|
||||||
|
|
||||||
|
_logger.LogInformation("Delete, Id:{Id}", model.Id);
|
||||||
|
|
||||||
|
if(_furnitureStorage.Delete(model) == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Delete operation failed");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка входного аргумента для методов Insert, Update и Delete
|
||||||
|
private void CheckModel(FurnitureBindingModel model, bool withParams = true)
|
||||||
|
{
|
||||||
|
if(model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
// При удалении параметру withParams передаём false
|
||||||
|
if (!withParams)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на наличие названия изделия
|
||||||
|
if(string.IsNullOrEmpty(model.FurnitureName))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Нет названия изделия", nameof(model.FurnitureName));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на наличие нормальной цены у изделия
|
||||||
|
if(model.Price <= 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Цена изделия должна быть больше 0", nameof(model.Price));
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Furniture. FurnitureName:{FurnitureName}. Price:{Price}. Id:{Id}",
|
||||||
|
model.FurnitureName, model.Price, model.Id);
|
||||||
|
|
||||||
|
// Проверка на наличие такого же изделия в списке
|
||||||
|
var element = _furnitureStorage.GetElement(new FurnitureSearchModel
|
||||||
|
{
|
||||||
|
FurnitureName = model.FurnitureName,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Если элемент найден и его Id не совпадает с Id объекта, переданного на вход
|
||||||
|
if(element != null && element.Id != model.Id)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Изделие с таким названием уже есть");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,174 @@
|
|||||||
|
using FurnitureAssemblyContracts.BindingModels;
|
||||||
|
using FurnitureAssemblyContracts.BusinessLogicsContracts;
|
||||||
|
using FurnitureAssemblyContracts.SearchModels;
|
||||||
|
using FurnitureAssemblyContracts.StoragesContracts;
|
||||||
|
using FurnitureAssemblyContracts.ViewModels;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.BussinessLogic
|
||||||
|
{
|
||||||
|
// Класс, реализующий логику для исполнителей
|
||||||
|
public class ImplementerLogic : IImplementerLogic
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
private readonly IImplementerStorage _implementerStorage;
|
||||||
|
|
||||||
|
// Конструктор
|
||||||
|
public ImplementerLogic(ILogger<ImplementerLogic> logger, IImplementerStorage implementerStorage)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_implementerStorage = implementerStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вывод всего отфильтрованного списка
|
||||||
|
public List<ImplementerViewModel>? ReadList(ImplementerSearchModel? model)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("ReadList. ImplementerFIO:{ImplementerFIO}. Id:{Id}", model?.ImplementerFIO, model?.Id);
|
||||||
|
|
||||||
|
// list хранит весь список в случае, если model пришло со значением null на вход метода
|
||||||
|
var list = model == null ? _implementerStorage.GetFullList() : _implementerStorage.GetFilteredList(model);
|
||||||
|
|
||||||
|
if (list == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ReadList return null list");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("ReadList. Count:{Count}", list.Count);
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вывод конкретного элемента
|
||||||
|
public ImplementerViewModel? ReadElement(ImplementerSearchModel model)
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("ReadList. ImplementerFIO:{ImplementerFIO}. Id:{Id}", model.ImplementerFIO, model?.Id);
|
||||||
|
|
||||||
|
var element = _implementerStorage.GetElement(model);
|
||||||
|
|
||||||
|
if (element == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ReadElement element not found");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("ReadElement find. Id:{Id}", element.Id);
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создание работника
|
||||||
|
public bool Create(ImplementerBindingModel model)
|
||||||
|
{
|
||||||
|
CheckModel(model);
|
||||||
|
|
||||||
|
if (_implementerStorage.Insert(model) == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Insert operation failed");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновление данных о работнике
|
||||||
|
public bool Update(ImplementerBindingModel model)
|
||||||
|
{
|
||||||
|
CheckModel(model);
|
||||||
|
|
||||||
|
if (_implementerStorage.Update(model) == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Update operation failed");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаление работника
|
||||||
|
public bool Delete(ImplementerBindingModel model)
|
||||||
|
{
|
||||||
|
CheckModel(model, false);
|
||||||
|
|
||||||
|
_logger.LogInformation("Delete. Id:{Id}", model.Id);
|
||||||
|
|
||||||
|
if (_implementerStorage.Delete(model) == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Delete operation failed");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка входного аргумента для методов Insert, Update и Delete
|
||||||
|
private void CheckModel(ImplementerBindingModel model, bool withParams = true)
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Так как при удалении передаём как параметр false
|
||||||
|
if (!withParams)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на наличие ФИО
|
||||||
|
if (string.IsNullOrEmpty(model.ImplementerFIO))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Отсутствие ФИО в учётной записи", nameof(model.ImplementerFIO));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на наличие пароля
|
||||||
|
if (string.IsNullOrEmpty(model.Password))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Отсутствие пароля в учётной записи", nameof(model.Password));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на наличие квалификации
|
||||||
|
if (model.Qualification <= 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Указана некорректная квалификация", nameof(model.Qualification));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на наличие квалификации
|
||||||
|
if (model.WorkExperience < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Указан некорректный стаж работы", nameof(model.WorkExperience));
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Implementer. ImplementerFIO:{ImplementerFIO}. Password:{Password}. " +
|
||||||
|
"Qualification:{Qualification}. WorkExperience:{ WorkExperience}. Id:{Id}",
|
||||||
|
model.ImplementerFIO, model.Password, model.Qualification, model.WorkExperience, model.Id);
|
||||||
|
|
||||||
|
// Для проверка на наличие такого же аккаунта
|
||||||
|
var element = _implementerStorage.GetElement(new ImplementerSearchModel
|
||||||
|
{
|
||||||
|
ImplementerFIO = model.ImplementerFIO,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Если элемент найден и его Id не совпадает с Id переданного объекта
|
||||||
|
if (element != null && element.Id != model.Id)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Исполнитель с таким именем уже есть");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
using FurnitureAssemblyContracts.BindingModels;
|
||||||
|
using FurnitureAssemblyContracts.BusinessLogicsContracts;
|
||||||
|
using FurnitureAssemblyContracts.SearchModels;
|
||||||
|
using FurnitureAssemblyContracts.StoragesContracts;
|
||||||
|
using FurnitureAssemblyContracts.ViewModels;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.BussinessLogic
|
||||||
|
{
|
||||||
|
// Класс, реализующий логику для сообщений
|
||||||
|
public class MessageInfoLogic : IMessageInfoLogic
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
private readonly IMessageInfoStorage _messageInfoStorage;
|
||||||
|
|
||||||
|
// Конструктор
|
||||||
|
public MessageInfoLogic(ILogger<MessageInfoLogic> logger, IMessageInfoStorage messageInfoStorage)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_messageInfoStorage = messageInfoStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<MessageInfoViewModel>? ReadList(MessageInfoSearchModel? model)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("ReadList. MessageId:{MessageId}", model?.MessageId);
|
||||||
|
|
||||||
|
// list хранит весь список в случае, если model пришло со значением null на вход метода
|
||||||
|
var list = model == null ? _messageInfoStorage.GetFullList() : _messageInfoStorage.GetFilteredList(model);
|
||||||
|
|
||||||
|
if (list == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ReadList return null list");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("ReadList. Count:{Count}", list.Count);
|
||||||
|
|
||||||
|
return list.OrderByDescending(x => x.DateDelivery).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Create(MessageInfoBindingModel model)
|
||||||
|
{
|
||||||
|
if (_messageInfoStorage.Insert(model) == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Insert operation failed");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,250 @@
|
|||||||
|
using FurnitureAssemblyBusinessLogic.MailWorker;
|
||||||
|
using FurnitureAssemblyContracts.BindingModels;
|
||||||
|
using FurnitureAssemblyContracts.BusinessLogicsContracts;
|
||||||
|
using FurnitureAssemblyContracts.SearchModels;
|
||||||
|
using FurnitureAssemblyContracts.StoragesContracts;
|
||||||
|
using FurnitureAssemblyContracts.ViewModels;
|
||||||
|
using FurnitureAssemblyDataModels.Enums;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.BussinessLogic
|
||||||
|
{
|
||||||
|
// Класс, реализующий логику для заказов
|
||||||
|
public class OrderLogic : IOrderLogic
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
private readonly IOrderStorage _orderStorage;
|
||||||
|
|
||||||
|
private readonly AbstractMailWorker _mailWorker;
|
||||||
|
|
||||||
|
private readonly IClientLogic _clientLogic;
|
||||||
|
|
||||||
|
private readonly object locker = new object();
|
||||||
|
|
||||||
|
// Конструктор
|
||||||
|
public OrderLogic(ILogger<OrderLogic> logger, IOrderStorage orderStorage, AbstractMailWorker mailWorker, IClientLogic clientLogic)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_orderStorage = orderStorage;
|
||||||
|
_mailWorker = mailWorker;
|
||||||
|
_clientLogic = clientLogic;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вывод отфильтрованного списка компонентов
|
||||||
|
public List<OrderViewModel>? ReadList(OrderSearchModel? model)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("ReadList. Id:{Id}", model?.Id);
|
||||||
|
|
||||||
|
// list хранит весь список в случае, если model пришло со значением null на вход метода
|
||||||
|
var list = model == null ? _orderStorage.GetFullList() : _orderStorage.GetFilteredList(model);
|
||||||
|
|
||||||
|
if (list == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ReadList return null list");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("ReadList. Count:{Count}", list.Count);
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вывод конкретного чека
|
||||||
|
public OrderViewModel? ReadElement(OrderSearchModel model)
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("ReadElement. Id:{Id}", model?.Id);
|
||||||
|
|
||||||
|
var element = _orderStorage.GetElement(model);
|
||||||
|
|
||||||
|
if (element == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ReadElement element not found");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("ReadElement find. Id:{Id}", element.Id);
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создание чека
|
||||||
|
public bool CreateOrder(OrderBindingModel model)
|
||||||
|
{
|
||||||
|
CheckModel(model);
|
||||||
|
|
||||||
|
if (model.Status != OrderStatus.Неизвестен)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Insert operation failed, incorrect order status");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
model.Status = OrderStatus.Принят;
|
||||||
|
|
||||||
|
var result = _orderStorage.Insert(model);
|
||||||
|
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
model.Status = OrderStatus.Неизвестен;
|
||||||
|
_logger.LogWarning("Insert operation failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SendOrderMessage(result.ClientId, $"Сборка мебели, Заказ №{result.Id}", $"Заказ №{result.Id} от {result.DateCreate} на сумму {result.Sum:0.00} принят");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TakeOrderInWork(OrderBindingModel model)
|
||||||
|
{
|
||||||
|
lock (locker)
|
||||||
|
{
|
||||||
|
return StatusUpdate(model, OrderStatus.Выполняется);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FinishOrder(OrderBindingModel model)
|
||||||
|
{
|
||||||
|
return StatusUpdate(model, OrderStatus.Готов);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DeliveryOrder(OrderBindingModel model)
|
||||||
|
{
|
||||||
|
return StatusUpdate(model, OrderStatus.Выдан);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на пустоту входного параметра
|
||||||
|
private void CheckModel(OrderBindingModel model, bool withParams = true)
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
// При удалении параметру withParams передаём false
|
||||||
|
if (!withParams)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на наличие товаров в заказе
|
||||||
|
if (model.Count <= 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("В заказе не может быть 0 изделий", nameof(model.Count));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на наличие нормальной суммарной стоимости чека
|
||||||
|
if (model.Sum <= 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Суммарная стоимость заказа должна быть больше 0", nameof(model.Sum));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка корректности id у изделий
|
||||||
|
if (model.FurnitureId < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Некорректный id у изделия", nameof(model.FurnitureId));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка корректности дат
|
||||||
|
if (model.DateCreate > model.DateImplement)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Дата создания должна быть более ранней, нежели дата завершения");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на клиента
|
||||||
|
if (model.ClientId < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Некорректный идентификатор у клиента", nameof(model.ClientId));
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Order. OrderId:{Id}. Sum:{Sum}. ClientId:{ClientId}. FurnitureId:{Id}", model.Id, model.Sum, model.ClientId, model.FurnitureId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновление статуса заказа
|
||||||
|
public bool StatusUpdate(OrderBindingModel model, OrderStatus newOrderStatus)
|
||||||
|
{
|
||||||
|
var viewModel = _orderStorage.GetElement(new OrderSearchModel { Id = model.Id });
|
||||||
|
|
||||||
|
// Если не смогли найти указанный заказ по его Id
|
||||||
|
if (viewModel == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на возможность обновления статуса на следующий
|
||||||
|
if (viewModel.Status + 1 != newOrderStatus)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Status update operation failed. New status " + newOrderStatus.ToString() + "incorrect");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
model.Status = newOrderStatus;
|
||||||
|
|
||||||
|
// Помещаем id работника, не забываем про него...
|
||||||
|
if (viewModel.ImplementerId.HasValue)
|
||||||
|
{
|
||||||
|
model.ImplementerId = viewModel.ImplementerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на выдачу
|
||||||
|
if (model.Status == OrderStatus.Выдан)
|
||||||
|
{
|
||||||
|
model.DateImplement = DateTime.Now;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
model.DateImplement = viewModel.DateImplement;
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckModel(model, false);
|
||||||
|
|
||||||
|
// Финальная проверка на возможность обновления
|
||||||
|
var result = _orderStorage.Update(model);
|
||||||
|
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
model.Status--;
|
||||||
|
|
||||||
|
_logger.LogWarning("Update operation failed");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SendOrderMessage(result.ClientId, $"Сборка мебели, Заказ №{result.Id}", $"Заказ №{model.Id} изменен статус на {result.Status}");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
using FurnitureAssemblyBusinessLogic.OfficePackage;
|
||||||
|
using FurnitureAssemblyBusinessLogic.OfficePackage.HelperModels;
|
||||||
|
using FurnitureAssemblyContracts.BindingModels;
|
||||||
|
using FurnitureAssemblyContracts.BusinessLogicsContracts;
|
||||||
|
using FurnitureAssemblyContracts.SearchModels;
|
||||||
|
using FurnitureAssemblyContracts.StoragesContracts;
|
||||||
|
using FurnitureAssemblyContracts.ViewModels;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.BussinessLogic
|
||||||
|
{
|
||||||
|
// Реализация бизнес-логики отчётов
|
||||||
|
public class ReportLogic : IReportLogic
|
||||||
|
{
|
||||||
|
private readonly IFurnitureStorage _furnitureStorage;
|
||||||
|
|
||||||
|
private readonly IOrderStorage _orderStorage;
|
||||||
|
|
||||||
|
private readonly AbstractSaveToExcel _saveToExcel;
|
||||||
|
|
||||||
|
private readonly AbstractSaveToWord _saveToWord;
|
||||||
|
|
||||||
|
private readonly AbstractSaveToPdf _saveToPdf;
|
||||||
|
|
||||||
|
// Инициализируем поля класса через контейнер
|
||||||
|
public ReportLogic(IFurnitureStorage furnitureStorage,
|
||||||
|
IOrderStorage orderStorage, AbstractSaveToExcel saveToExcel,
|
||||||
|
AbstractSaveToWord saveToWord, AbstractSaveToPdf saveToPdf)
|
||||||
|
{
|
||||||
|
_furnitureStorage = furnitureStorage;
|
||||||
|
_orderStorage = orderStorage;
|
||||||
|
|
||||||
|
_saveToExcel = saveToExcel;
|
||||||
|
_saveToWord = saveToWord;
|
||||||
|
_saveToPdf = saveToPdf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получение списка компонент с указанием, в каких изделиях используются
|
||||||
|
public List<ReportFurnitureWorkPieceViewModel> GetFurnitureWorkPiece()
|
||||||
|
{
|
||||||
|
var furnitures = _furnitureStorage.GetFullList();
|
||||||
|
|
||||||
|
var list = new List<ReportFurnitureWorkPieceViewModel>();
|
||||||
|
|
||||||
|
foreach (var furniture in furnitures)
|
||||||
|
{
|
||||||
|
var record = new ReportFurnitureWorkPieceViewModel
|
||||||
|
{
|
||||||
|
FurnitureName = furniture.FurnitureName,
|
||||||
|
WorkPieces = new List<(string, int)>(),
|
||||||
|
TotalCount = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var workPiece in furniture.FurnitureWorkPieces)
|
||||||
|
{
|
||||||
|
record.WorkPieces.Add(new(workPiece.Value.Item1.WorkPieceName, workPiece.Value.Item2));
|
||||||
|
record.TotalCount += workPiece.Value.Item2;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Add(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получение списка заказов за определённый период
|
||||||
|
public List<ReportOrdersViewModel> GetOrders(ReportBindingModel model)
|
||||||
|
{
|
||||||
|
return _orderStorage.GetFilteredList(new OrderSearchModel { DateFrom = model.DateFrom, DateTo = model.DateTo })
|
||||||
|
.Select(x => new ReportOrdersViewModel
|
||||||
|
{
|
||||||
|
Id = x.Id,
|
||||||
|
DateCreate = x.DateCreate,
|
||||||
|
FurnitureName = x.FurnitureName,
|
||||||
|
Sum = x.Sum,
|
||||||
|
OrderStatus = x.Status.ToString()
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сохранение мебели в файл-Word
|
||||||
|
public void SaveFurnituresToWordFile(ReportBindingModel model)
|
||||||
|
{
|
||||||
|
_saveToWord.CreateDoc(new WordInfo
|
||||||
|
{
|
||||||
|
FileName = model.FileName,
|
||||||
|
Title = "Список изделий",
|
||||||
|
Furnitures = _furnitureStorage.GetFullList()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сохранение заготовок с указанием изделий в файл_Excel
|
||||||
|
public void SaveFurnitureWorkPieceToExcelFile(ReportBindingModel model)
|
||||||
|
{
|
||||||
|
_saveToExcel.CreateReport(new ExcelInfo
|
||||||
|
{
|
||||||
|
FileName = model.FileName,
|
||||||
|
Title = "Список заготовок",
|
||||||
|
FurnitureWorkPieces = GetFurnitureWorkPiece()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сохранение заказов в файл-Pdf
|
||||||
|
public void SaveOrdersToPdfFile(ReportBindingModel model)
|
||||||
|
{
|
||||||
|
_saveToPdf.CreateDoc(new PdfInfo
|
||||||
|
{
|
||||||
|
FileName = model.FileName,
|
||||||
|
Title = "Список заказов",
|
||||||
|
DateFrom = model.DateFrom!.Value,
|
||||||
|
DateTo = model.DateTo!.Value,
|
||||||
|
Orders = GetOrders(model)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,166 @@
|
|||||||
|
using FurnitureAssemblyContracts.BindingModels;
|
||||||
|
using FurnitureAssemblyContracts.BusinessLogicsContracts;
|
||||||
|
using FurnitureAssemblyContracts.SearchModels;
|
||||||
|
using FurnitureAssemblyContracts.ViewModels;
|
||||||
|
using FurnitureAssemblyDataModels.Enums;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.BussinessLogic
|
||||||
|
{
|
||||||
|
public class WorkModeling : IWorkProcess
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
private readonly Random _rnd;
|
||||||
|
|
||||||
|
private IOrderLogic? _orderLogic;
|
||||||
|
|
||||||
|
// Конструктор
|
||||||
|
public WorkModeling(ILogger<WorkModeling> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_rnd = new Random(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DoWork(IImplementerLogic implementerLogic, IOrderLogic orderLogic)
|
||||||
|
{
|
||||||
|
_orderLogic = orderLogic;
|
||||||
|
var implementers = implementerLogic.ReadList(null);
|
||||||
|
|
||||||
|
if (implementers == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("DoWork. Implementers is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var orders = _orderLogic.ReadList(new OrderSearchModel
|
||||||
|
{
|
||||||
|
Status = OrderStatus.Принят
|
||||||
|
});
|
||||||
|
|
||||||
|
if (orders == null || orders.Count == 0)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Dowork. Orders is null or empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogDebug("DoWork for {count} orders", orders.Count);
|
||||||
|
|
||||||
|
foreach (var implementer in implementers)
|
||||||
|
{
|
||||||
|
Task.Run(() => WorkerWorkAsync(implementer, orders));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Имитация работы исполнителя
|
||||||
|
private async Task WorkerWorkAsync(ImplementerViewModel implementer, List<OrderViewModel> orders)
|
||||||
|
{
|
||||||
|
if (_orderLogic == null || implementer == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RunOrderInWork(implementer);
|
||||||
|
|
||||||
|
await Task.Run(() =>
|
||||||
|
{
|
||||||
|
foreach (var order in orders)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogDebug("DoWork. Worker {Id} try get order {Order}", implementer.Id, order.Id);
|
||||||
|
|
||||||
|
// Пытаемся назначить заказ на исполнителя
|
||||||
|
_orderLogic.TakeOrderInWork(new OrderBindingModel
|
||||||
|
{
|
||||||
|
Id = order.Id,
|
||||||
|
ImplementerId = implementer.Id
|
||||||
|
});
|
||||||
|
|
||||||
|
// Работу работаем, делаем-делаем
|
||||||
|
Thread.Sleep(implementer.WorkExperience * order.Count);
|
||||||
|
|
||||||
|
_logger.LogDebug("DoWork. Worker {Id} finish order {Order}", implementer.Id, order.Id);
|
||||||
|
|
||||||
|
_orderLogic.FinishOrder(new OrderBindingModel
|
||||||
|
{
|
||||||
|
Id = order.Id
|
||||||
|
});
|
||||||
|
|
||||||
|
// Усёёё отдыхаем
|
||||||
|
Thread.Sleep(implementer.Qualification);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Игнорируем ошибку, если с заказом что-то случится
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "Error try get work");
|
||||||
|
}
|
||||||
|
|
||||||
|
// В случае ошибки прервём выполнение имитации работы
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error while do work");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ищем заказ, который уже во всю в работе (вдруг прервали исполнителя)
|
||||||
|
private async Task RunOrderInWork(ImplementerViewModel implementer)
|
||||||
|
{
|
||||||
|
if (_orderLogic == null || implementer == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var runOrder = await Task.Run(() => _orderLogic.ReadElement(new OrderSearchModel
|
||||||
|
{
|
||||||
|
ImplementerId = implementer.Id,
|
||||||
|
Status = OrderStatus.Выполняется
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (runOrder == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogDebug("DoWork {Id} back to order {Order}", implementer.Id, runOrder.Id);
|
||||||
|
|
||||||
|
// Доделываем работу
|
||||||
|
Thread.Sleep(implementer.WorkExperience * runOrder.Count);
|
||||||
|
|
||||||
|
_logger.LogDebug("DoWork. Worker {Id} finish order {Order}", implementer.Id, runOrder.Id);
|
||||||
|
|
||||||
|
_orderLogic.FinishOrder(new OrderBindingModel
|
||||||
|
{
|
||||||
|
Id = runOrder.Id
|
||||||
|
});
|
||||||
|
|
||||||
|
// Отдыхаем, хватит работы
|
||||||
|
Thread.Sleep(implementer.Qualification);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Заказа может не быть, просто игнорируем ошибку
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "Error try get work");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Просто возникнет тупая ошибка, тогда заканчиваем выполнение имитации
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error while do work");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,161 @@
|
|||||||
|
using FurnitureAssemblyContracts.BindingModels;
|
||||||
|
using FurnitureAssemblyContracts.BusinessLogicsContracts;
|
||||||
|
using FurnitureAssemblyContracts.SearchModels;
|
||||||
|
using FurnitureAssemblyContracts.StoragesContracts;
|
||||||
|
using FurnitureAssemblyContracts.ViewModels;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.BussinessLogic
|
||||||
|
{
|
||||||
|
// Класс, реализующий логику для заготовок
|
||||||
|
public class WorkPieceLogic : IWorkPieceLogic
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
private readonly IWorkPieceStorage _workPieceStorage;
|
||||||
|
|
||||||
|
// Конструктор
|
||||||
|
public WorkPieceLogic(ILogger<WorkPieceLogic> logger, IWorkPieceStorage workPieceStorage)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_workPieceStorage = workPieceStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вывод отфильтрованного списка компонентов
|
||||||
|
public List<WorkPieceViewModel>? ReadList(WorkPieceSearchModel? model)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("ReadList. WorkPieceName:{WorkPieceName}. Id:{Id}", model?.WorkPieceName, model?.Id);
|
||||||
|
|
||||||
|
// list хранит весь список в случае, если model пришло со значением null на вход метода
|
||||||
|
var list = model == null ? _workPieceStorage.GetFullList() : _workPieceStorage.GetFilteredList(model);
|
||||||
|
|
||||||
|
if(list == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ReadList return null list");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("ReadList. Count:{Count}", list.Count);
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вывод конкретной заготовки
|
||||||
|
public WorkPieceViewModel? ReadElement(WorkPieceSearchModel model)
|
||||||
|
{
|
||||||
|
if(model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("ReadElement. WorkPieceName:{WorkPieceName}. Id:{Id}", model.WorkPieceName, model.Id);
|
||||||
|
|
||||||
|
var element = _workPieceStorage.GetElement(model);
|
||||||
|
|
||||||
|
if(element == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ReadElement element not found");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("ReadElement find. Id:{Id}", element.Id);
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создание заготовки
|
||||||
|
public bool Create(WorkPieceBindingModel model)
|
||||||
|
{
|
||||||
|
CheckModel(model);
|
||||||
|
|
||||||
|
if(_workPieceStorage.Insert(model) == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("insert operation failed");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновление заготовки
|
||||||
|
public bool Update(WorkPieceBindingModel model)
|
||||||
|
{
|
||||||
|
CheckModel(model);
|
||||||
|
|
||||||
|
if(_workPieceStorage.Update(model) == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Update operation failed");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаление заготовки
|
||||||
|
public bool Delete(WorkPieceBindingModel model)
|
||||||
|
{
|
||||||
|
CheckModel(model, false);
|
||||||
|
|
||||||
|
_logger.LogInformation("Delete. Id:{Id}", model.Id);
|
||||||
|
|
||||||
|
if(_workPieceStorage.Delete(model) == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Delete operation failed");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка входного аргумента для методов Insert, Update и Delete
|
||||||
|
private void CheckModel(WorkPieceBindingModel model, bool withParams = true)
|
||||||
|
{
|
||||||
|
if(model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
// При удалении передаём как параметр false
|
||||||
|
if (!withParams)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на наличие названия заготовки
|
||||||
|
if (string.IsNullOrEmpty(model.WorkPieceName))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Нет названия заготовки", nameof(model.WorkPieceName));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка на наличие нормальной цены у заготовки
|
||||||
|
if(model.Cost <= 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Цена заготовки должна быть больше 0", nameof(model.Cost));
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("WorkPiece. WorkPieceName:{WorkPieceName}. Cost:{Cost}. Id{Id}",
|
||||||
|
model.WorkPieceName, model.Cost, model.Id);
|
||||||
|
|
||||||
|
// Проверка на наличие такой же заготовки в списке
|
||||||
|
var element = _workPieceStorage.GetElement(new WorkPieceSearchModel
|
||||||
|
{
|
||||||
|
WorkPieceName = model.WorkPieceName,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Если элемент найден и его Id не совпадает с Id переданного объекта
|
||||||
|
if(element != null && element.Id != model.Id)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Заготовка с таким названием уже есть");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="DocumentFormat.OpenXml" Version="2.20.0" />
|
||||||
|
<PackageReference Include="MailKit" Version="4.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
|
||||||
|
<PackageReference Include="MigraDocCore.DocumentObjectModel" Version="1.3.63" />
|
||||||
|
<PackageReference Include="MigraDocCore.Rendering" Version="1.3.63" />
|
||||||
|
<PackageReference Include="PDFsharp-MigraDoc-GDI" Version="1.50.5147" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\FurnitureAssemblyContracts\FurnitureAssemblyContracts.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -0,0 +1,107 @@
|
|||||||
|
using FurnitureAssemblyContracts.BindingModels;
|
||||||
|
using FurnitureAssemblyContracts.BusinessLogicsContracts;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.MailWorker
|
||||||
|
{
|
||||||
|
public abstract class AbstractMailWorker
|
||||||
|
{
|
||||||
|
protected string _mailLogin = string.Empty;
|
||||||
|
|
||||||
|
protected string _mailPassword = string.Empty;
|
||||||
|
|
||||||
|
protected string _smtpClientHost = string.Empty;
|
||||||
|
|
||||||
|
protected int _smtpClientPort;
|
||||||
|
|
||||||
|
protected string _popHost = string.Empty;
|
||||||
|
|
||||||
|
protected int _popPort;
|
||||||
|
|
||||||
|
private readonly IMessageInfoLogic _messageInfoLogic;
|
||||||
|
|
||||||
|
private readonly IClientLogic _clientLogic;
|
||||||
|
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
public AbstractMailWorker(ILogger<AbstractMailWorker> logger, IMessageInfoLogic messageInfoLogic, IClientLogic clientLogic)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_messageInfoLogic = messageInfoLogic;
|
||||||
|
_clientLogic = clientLogic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MailConfig(MailConfigBindingModel config)
|
||||||
|
{
|
||||||
|
_mailLogin = config.MailLogin;
|
||||||
|
_mailPassword = config.MailPassword;
|
||||||
|
_smtpClientHost = config.SmtpClientHost;
|
||||||
|
_smtpClientPort = config.SmtpClientPort;
|
||||||
|
_popHost = config.PopHost;
|
||||||
|
_popPort = config.PopPort;
|
||||||
|
|
||||||
|
_logger.LogDebug("Config: {login}, {password}, {clientHost}, {clientPOrt}, { popHost}, { popPort}",
|
||||||
|
_mailLogin, _mailPassword, _smtpClientHost, _smtpClientPort, _popHost, _popPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void MailSendAsync(MailSendInfoBindingModel info)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_mailLogin) || string.IsNullOrEmpty(_mailPassword))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(_smtpClientHost) || _smtpClientPort == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(info.MailAddress) || string.IsNullOrEmpty(info.Subject) || string.IsNullOrEmpty(info.Text))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogDebug("Send Mail: {To}, {Subject}", info.MailAddress, info.Subject);
|
||||||
|
|
||||||
|
await SendMailAsync(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void MailCheck()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_mailLogin) || string.IsNullOrEmpty(_mailPassword))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(_popHost) || _popPort == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_messageInfoLogic == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var list = await ReceiveMailAsync();
|
||||||
|
|
||||||
|
_logger.LogDebug("Check Mail: {Count} new mails", list.Count);
|
||||||
|
|
||||||
|
foreach (var mail in list)
|
||||||
|
{
|
||||||
|
mail.ClientId = _clientLogic.ReadElement(new() { Email = mail.SenderName })?.Id;
|
||||||
|
|
||||||
|
_messageInfoLogic.Create(mail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Task SendMailAsync(MailSendInfoBindingModel info);
|
||||||
|
|
||||||
|
protected abstract Task<List<MessageInfoBindingModel>> ReceiveMailAsync();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
using FurnitureAssemblyContracts.BindingModels;
|
||||||
|
using FurnitureAssemblyContracts.BusinessLogicsContracts;
|
||||||
|
using MailKit.Net.Pop3;
|
||||||
|
using MailKit.Security;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Mail;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.MailWorker
|
||||||
|
{
|
||||||
|
public class MailKitWorker : AbstractMailWorker
|
||||||
|
{
|
||||||
|
public MailKitWorker(ILogger<MailKitWorker> logger, IMessageInfoLogic messageInfoLogic, IClientLogic clientLogic)
|
||||||
|
: base(logger, messageInfoLogic, clientLogic) { }
|
||||||
|
|
||||||
|
protected override async Task SendMailAsync(MailSendInfoBindingModel info)
|
||||||
|
{
|
||||||
|
using var objMailMessage = new MailMessage();
|
||||||
|
|
||||||
|
using var objSmtpClient = new SmtpClient(_smtpClientHost, _smtpClientPort);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
objMailMessage.From = new MailAddress(_mailLogin);
|
||||||
|
objMailMessage.To.Add(new MailAddress(info.MailAddress));
|
||||||
|
objMailMessage.Subject = info.Subject;
|
||||||
|
objMailMessage.Body = info.Text;
|
||||||
|
objMailMessage.SubjectEncoding = Encoding.UTF8;
|
||||||
|
objMailMessage.BodyEncoding = Encoding.UTF8;
|
||||||
|
|
||||||
|
objSmtpClient.UseDefaultCredentials = false;
|
||||||
|
objSmtpClient.EnableSsl = true;
|
||||||
|
objSmtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
|
||||||
|
objSmtpClient.Credentials = new NetworkCredential(_mailLogin, _mailPassword);
|
||||||
|
|
||||||
|
await Task.Run(() => objSmtpClient.Send(objMailMessage));
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task<List<MessageInfoBindingModel>> ReceiveMailAsync()
|
||||||
|
{
|
||||||
|
var list = new List<MessageInfoBindingModel>();
|
||||||
|
|
||||||
|
using var client = new Pop3Client();
|
||||||
|
|
||||||
|
await Task.Run(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client.Connect(_popHost, _popPort, SecureSocketOptions.SslOnConnect);
|
||||||
|
|
||||||
|
client.Authenticate(_mailLogin, _mailPassword);
|
||||||
|
|
||||||
|
for (int i = 0; i < client.Count; i++)
|
||||||
|
{
|
||||||
|
var message = client.GetMessage(i);
|
||||||
|
|
||||||
|
foreach (var mail in message.From.Mailboxes)
|
||||||
|
{
|
||||||
|
list.Add(new MessageInfoBindingModel
|
||||||
|
{
|
||||||
|
DateDelivery = message.Date.DateTime,
|
||||||
|
MessageId = message.MessageId,
|
||||||
|
SenderName = mail.Address,
|
||||||
|
Subject = message.Subject,
|
||||||
|
Body = message.TextBody
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (AuthenticationException)
|
||||||
|
{ }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
client.Disconnect(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
using FurnitureAssemblyBusinessLogic.OfficePackage.HelperEnums;
|
||||||
|
using FurnitureAssemblyBusinessLogic.OfficePackage.HelperModels;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.OfficePackage
|
||||||
|
{
|
||||||
|
public abstract class AbstractSaveToExcel
|
||||||
|
{
|
||||||
|
// Создание отчета. Описание методов ниже
|
||||||
|
public void CreateReport(ExcelInfo info)
|
||||||
|
{
|
||||||
|
CreateExcel(info);
|
||||||
|
|
||||||
|
InsertCellInWorksheet(new ExcelCellParameters
|
||||||
|
{
|
||||||
|
ColumnName = "A",
|
||||||
|
RowIndex = 1,
|
||||||
|
Text = info.Title,
|
||||||
|
StyleInfo = ExcelStyleInfoType.Title
|
||||||
|
});
|
||||||
|
|
||||||
|
MergeCells(new ExcelMergeParameters
|
||||||
|
{
|
||||||
|
CellFromName = "A1",
|
||||||
|
CellToName = "C1"
|
||||||
|
});
|
||||||
|
|
||||||
|
uint rowIndex = 2;
|
||||||
|
|
||||||
|
foreach (var fwp in info.FurnitureWorkPieces)
|
||||||
|
{
|
||||||
|
InsertCellInWorksheet(new ExcelCellParameters
|
||||||
|
{
|
||||||
|
ColumnName = "A",
|
||||||
|
RowIndex = rowIndex,
|
||||||
|
Text = fwp.FurnitureName,
|
||||||
|
StyleInfo = ExcelStyleInfoType.Text
|
||||||
|
});
|
||||||
|
|
||||||
|
rowIndex++;
|
||||||
|
|
||||||
|
foreach (var (WorkPiece, Count) in fwp.WorkPieces)
|
||||||
|
{
|
||||||
|
InsertCellInWorksheet(new ExcelCellParameters
|
||||||
|
{
|
||||||
|
ColumnName = "B",
|
||||||
|
RowIndex = rowIndex,
|
||||||
|
Text = WorkPiece,
|
||||||
|
StyleInfo = ExcelStyleInfoType.TextWithBorder
|
||||||
|
});
|
||||||
|
|
||||||
|
InsertCellInWorksheet(new ExcelCellParameters
|
||||||
|
{
|
||||||
|
ColumnName = "C",
|
||||||
|
RowIndex = rowIndex,
|
||||||
|
Text = Count.ToString(),
|
||||||
|
StyleInfo = ExcelStyleInfoType.TextWithBorder
|
||||||
|
});
|
||||||
|
|
||||||
|
rowIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertCellInWorksheet(new ExcelCellParameters
|
||||||
|
{
|
||||||
|
ColumnName = "A",
|
||||||
|
RowIndex = rowIndex,
|
||||||
|
Text = "Итого",
|
||||||
|
StyleInfo = ExcelStyleInfoType.Text
|
||||||
|
});
|
||||||
|
|
||||||
|
InsertCellInWorksheet(new ExcelCellParameters
|
||||||
|
{
|
||||||
|
ColumnName = "C",
|
||||||
|
RowIndex = rowIndex,
|
||||||
|
Text = fwp.TotalCount.ToString(),
|
||||||
|
StyleInfo = ExcelStyleInfoType.Text
|
||||||
|
});
|
||||||
|
|
||||||
|
rowIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveExcel(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создание Excel-файла
|
||||||
|
protected abstract void CreateExcel(ExcelInfo info);
|
||||||
|
|
||||||
|
// Добавляем новую ячейку в лист
|
||||||
|
protected abstract void InsertCellInWorksheet(ExcelCellParameters excelParams);
|
||||||
|
|
||||||
|
// Объединение ячеек
|
||||||
|
protected abstract void MergeCells(ExcelMergeParameters excelParams);
|
||||||
|
|
||||||
|
// Сохранение файла
|
||||||
|
protected abstract void SaveExcel(ExcelInfo info);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
using FurnitureAssemblyBusinessLogic.OfficePackage.HelperEnums;
|
||||||
|
using FurnitureAssemblyBusinessLogic.OfficePackage.HelperModels;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.OfficePackage
|
||||||
|
{
|
||||||
|
public abstract class AbstractSaveToPdf
|
||||||
|
{
|
||||||
|
// Публичный метод создания документа. Описание методов ниже
|
||||||
|
public void CreateDoc(PdfInfo info)
|
||||||
|
{
|
||||||
|
CreatePdf(info);
|
||||||
|
|
||||||
|
CreateParagraph(new PdfParagraph
|
||||||
|
{
|
||||||
|
Text = info.Title,
|
||||||
|
Style = "NormalTitle",
|
||||||
|
ParagraphAlignment = PdfParagraphAlignmentType.Center
|
||||||
|
});
|
||||||
|
|
||||||
|
CreateParagraph(new PdfParagraph
|
||||||
|
{
|
||||||
|
Text = $"с {info.DateFrom.ToShortDateString()} по {info.DateTo.ToShortDateString()}",
|
||||||
|
Style = "Normal",
|
||||||
|
ParagraphAlignment = PdfParagraphAlignmentType.Center
|
||||||
|
});
|
||||||
|
|
||||||
|
CreateTable(new List<string> { "2cm", "3cm", "6cm", "3cm", "3cm" });
|
||||||
|
|
||||||
|
CreateRow(new PdfRowParameters
|
||||||
|
{
|
||||||
|
Texts = new List<string> { "Номер", "Дата заказа", "Изделие", "Сумма", "Статус заказа" },
|
||||||
|
Style = "NormalTitle",
|
||||||
|
ParagraphAlignment = PdfParagraphAlignmentType.Center
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (var order in info.Orders)
|
||||||
|
{
|
||||||
|
CreateRow(new PdfRowParameters
|
||||||
|
{
|
||||||
|
Texts = new List<string> { order.Id.ToString(), order.DateCreate.ToShortDateString(), order.FurnitureName, order.Sum.ToString(), order.OrderStatus },
|
||||||
|
Style = "Normal",
|
||||||
|
ParagraphAlignment = PdfParagraphAlignmentType.Left
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateParagraph(new PdfParagraph
|
||||||
|
{
|
||||||
|
Text = $"\nИтого: {info.Orders.Sum(x => x.Sum)}\t",
|
||||||
|
Style = "Normal",
|
||||||
|
ParagraphAlignment = PdfParagraphAlignmentType.Right
|
||||||
|
});
|
||||||
|
|
||||||
|
SavePdf(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Создание Pdf-файла
|
||||||
|
protected abstract void CreatePdf(PdfInfo info);
|
||||||
|
|
||||||
|
/// Создание параграфа с текстом
|
||||||
|
protected abstract void CreateParagraph(PdfParagraph paragraph);
|
||||||
|
|
||||||
|
/// Создание таблицы
|
||||||
|
protected abstract void CreateTable(List<string> columns);
|
||||||
|
|
||||||
|
/// Создание и заполнение строки
|
||||||
|
protected abstract void CreateRow(PdfRowParameters rowParameters);
|
||||||
|
|
||||||
|
/// Сохранение файла
|
||||||
|
protected abstract void SavePdf(PdfInfo info);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
using FurnitureAssemblyBusinessLogic.OfficePackage.HelperEnums;
|
||||||
|
using FurnitureAssemblyBusinessLogic.OfficePackage.HelperModels;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.OfficePackage
|
||||||
|
{
|
||||||
|
public abstract class AbstractSaveToWord
|
||||||
|
{
|
||||||
|
// Метод создания документа
|
||||||
|
public void CreateDoc(WordInfo info)
|
||||||
|
{
|
||||||
|
CreateWord(info);
|
||||||
|
|
||||||
|
// Создание ряда абзацев
|
||||||
|
CreateParagraph(new WordParagraph
|
||||||
|
{
|
||||||
|
Texts = new List<(string, WordTextProperties)> { (info.Title, new WordTextProperties { Bold = true, Size = "24", }) },
|
||||||
|
TextProperties = new WordTextProperties
|
||||||
|
{
|
||||||
|
Size = "24",
|
||||||
|
JustificationType = WordJustificationType.Center
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Заполнение абзацев текстом
|
||||||
|
foreach (var furniture in info.Furnitures)
|
||||||
|
{
|
||||||
|
CreateParagraph(new WordParagraph
|
||||||
|
{
|
||||||
|
Texts = new List<(string, WordTextProperties)> { (furniture.FurnitureName + " ", new WordTextProperties { Bold = true, Size = "24", }),
|
||||||
|
(furniture.Price.ToString(), new WordTextProperties { Size = "24" }) },
|
||||||
|
TextProperties = new WordTextProperties
|
||||||
|
{
|
||||||
|
Size = "24",
|
||||||
|
JustificationType = WordJustificationType.Both
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveWord(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создание Doc-файла
|
||||||
|
protected abstract void CreateWord(WordInfo info);
|
||||||
|
|
||||||
|
// Создание абзаца с текстом
|
||||||
|
protected abstract void CreateParagraph(WordParagraph paragraph);
|
||||||
|
|
||||||
|
// Сохранение файла
|
||||||
|
protected abstract void SaveWord(WordInfo info);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.OfficePackage.HelperEnums
|
||||||
|
{
|
||||||
|
// Вспомогательное перечисление для оформления excel
|
||||||
|
public enum ExcelStyleInfoType
|
||||||
|
{
|
||||||
|
// Заголовок
|
||||||
|
Title,
|
||||||
|
|
||||||
|
// Просто текст
|
||||||
|
Text,
|
||||||
|
|
||||||
|
// Текст в рамке
|
||||||
|
TextWithBorder
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.OfficePackage.HelperEnums
|
||||||
|
{
|
||||||
|
// Вспомогательное перечисление для оформления pdf документа
|
||||||
|
public enum PdfParagraphAlignmentType
|
||||||
|
{
|
||||||
|
// Либо по центру
|
||||||
|
Center,
|
||||||
|
|
||||||
|
// Либо с левого края
|
||||||
|
Left,
|
||||||
|
|
||||||
|
// Либо с правого края
|
||||||
|
Right
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.OfficePackage.HelperEnums
|
||||||
|
{
|
||||||
|
// Вспомогательное перечисление для настройки формата word документа
|
||||||
|
public enum WordJustificationType
|
||||||
|
{
|
||||||
|
// Выравниваем либо по центру
|
||||||
|
Center,
|
||||||
|
|
||||||
|
// Либо на всю ширину
|
||||||
|
Both
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
using FurnitureAssemblyBusinessLogic.OfficePackage.HelperEnums;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.OfficePackage.HelperModels
|
||||||
|
{
|
||||||
|
// Информация по ячейке в таблице excel
|
||||||
|
public class ExcelCellParameters
|
||||||
|
{
|
||||||
|
// Название колонки
|
||||||
|
public string ColumnName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
// Строка
|
||||||
|
public uint RowIndex { get; set; }
|
||||||
|
|
||||||
|
// Тект в ячейке
|
||||||
|
public string Text { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
// Геттер для того, чтобы не искать каждый раз
|
||||||
|
public string CellReference => $"{ColumnName}{RowIndex}";
|
||||||
|
|
||||||
|
// В каком стиле выводить информацию
|
||||||
|
public ExcelStyleInfoType StyleInfo { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
using FurnitureAssemblyContracts.ViewModels;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.OfficePackage.HelperModels
|
||||||
|
{
|
||||||
|
// Информация по excel файлу, который хотим создать
|
||||||
|
public class ExcelInfo
|
||||||
|
{
|
||||||
|
// Название файла
|
||||||
|
public string FileName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
// Заголовок
|
||||||
|
public string Title { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
// Список заготовок по изделиям
|
||||||
|
public List<ReportFurnitureWorkPieceViewModel> FurnitureWorkPieces { get; set; } = new();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.OfficePackage.HelperModels
|
||||||
|
{
|
||||||
|
// Информация для объединения ячеек
|
||||||
|
public class ExcelMergeParameters
|
||||||
|
{
|
||||||
|
public string CellFromName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string CellToName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
// Геттер для указания диапазона для объединения, чтобы каждый раз его не вычислять
|
||||||
|
public string Merge => $"{CellFromName}:{CellToName}";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
using FurnitureAssemblyContracts.ViewModels;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.OfficePackage.HelperModels
|
||||||
|
{
|
||||||
|
// Общая информация по pdf файлу
|
||||||
|
public class PdfInfo
|
||||||
|
{
|
||||||
|
public string FileName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string Title { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public DateTime DateFrom { get; set; }
|
||||||
|
|
||||||
|
public DateTime DateTo { get; set; }
|
||||||
|
|
||||||
|
// Перечень заказов за указанный период для вывода/сохранения
|
||||||
|
public List<ReportOrdersViewModel> Orders { get; set; } = new();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
using FurnitureAssemblyBusinessLogic.OfficePackage.HelperEnums;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.OfficePackage.HelperModels
|
||||||
|
{
|
||||||
|
// Информация п параграфу в pdf документе
|
||||||
|
public class PdfParagraph
|
||||||
|
{
|
||||||
|
public string Text { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string Style { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
// Информация по выравниванию текста в параграфе
|
||||||
|
public PdfParagraphAlignmentType ParagraphAlignment { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
using FurnitureAssemblyBusinessLogic.OfficePackage.HelperEnums;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.OfficePackage.HelperModels
|
||||||
|
{
|
||||||
|
// Информация по параметрам строк таблицы
|
||||||
|
public class PdfRowParameters
|
||||||
|
{
|
||||||
|
// Набор текстов
|
||||||
|
public List<string> Texts { get; set; } = new();
|
||||||
|
|
||||||
|
// Стиль к текстам
|
||||||
|
public string Style { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
// Как выравниваем
|
||||||
|
public PdfParagraphAlignmentType ParagraphAlignment { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
using FurnitureAssemblyContracts.ViewModels;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.OfficePackage.HelperModels
|
||||||
|
{
|
||||||
|
// Общая информация по документу
|
||||||
|
public class WordInfo
|
||||||
|
{
|
||||||
|
public string FileName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string Title { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
// Cписок заготовок для вывода и сохранения
|
||||||
|
public List<FurnitureViewModel> Furnitures { get; set; } = new();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.OfficePackage.HelperModels
|
||||||
|
{
|
||||||
|
// Модель параграфов, которые есть в тексте
|
||||||
|
public class WordParagraph
|
||||||
|
{
|
||||||
|
// Набор текстов в абзаце (для случая, если в абзаце текст разных стилей)
|
||||||
|
public List<(string, WordTextProperties)> Texts { get; set; } = new();
|
||||||
|
|
||||||
|
// Cвойства параграфа, если они есть
|
||||||
|
public WordTextProperties? TextProperties { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
using FurnitureAssemblyBusinessLogic.OfficePackage.HelperEnums;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.OfficePackage.HelperModels
|
||||||
|
{
|
||||||
|
// Модель свойств текста, которые нам нужны в Word документе
|
||||||
|
public class WordTextProperties
|
||||||
|
{
|
||||||
|
// Размер текста
|
||||||
|
public string Size { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
// Надо ли делать его жирным
|
||||||
|
public bool Bold { get; set; }
|
||||||
|
|
||||||
|
// Выравнивание
|
||||||
|
public WordJustificationType JustificationType { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,393 @@
|
|||||||
|
using DocumentFormat.OpenXml.Office2010.Excel;
|
||||||
|
using DocumentFormat.OpenXml.Office2013.Excel;
|
||||||
|
using DocumentFormat.OpenXml.Packaging;
|
||||||
|
using DocumentFormat.OpenXml.Spreadsheet;
|
||||||
|
using DocumentFormat.OpenXml;
|
||||||
|
using FurnitureAssemblyBusinessLogic.OfficePackage.HelperEnums;
|
||||||
|
using FurnitureAssemblyBusinessLogic.OfficePackage.HelperModels;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.OfficePackage.Implements
|
||||||
|
{
|
||||||
|
// Реализация абстрактного класса создания Excel документа
|
||||||
|
public class SaveToExcel : AbstractSaveToExcel
|
||||||
|
{
|
||||||
|
private SpreadsheetDocument? _spreadsheetDocument;
|
||||||
|
|
||||||
|
private SharedStringTablePart? _shareStringPart;
|
||||||
|
|
||||||
|
private Worksheet? _worksheet;
|
||||||
|
|
||||||
|
// Настройка стилей для файла
|
||||||
|
private static void CreateStyles(WorkbookPart workbookpart)
|
||||||
|
{
|
||||||
|
var sp = workbookpart.AddNewPart<WorkbookStylesPart>();
|
||||||
|
sp.Stylesheet = new Stylesheet();
|
||||||
|
|
||||||
|
// Настройка шрифта простого текста
|
||||||
|
var fonts = new Fonts() { Count = 2U, KnownFonts = true };
|
||||||
|
|
||||||
|
var fontUsual = new Font();
|
||||||
|
fontUsual.Append(new FontSize() { Val = 12D });
|
||||||
|
fontUsual.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color() { Theme = 1U });
|
||||||
|
fontUsual.Append(new FontName() { Val = "Times New Roman" });
|
||||||
|
fontUsual.Append(new FontFamilyNumbering() { Val = 2 });
|
||||||
|
fontUsual.Append(new FontScheme() { Val = FontSchemeValues.Minor });
|
||||||
|
|
||||||
|
// Настройка шрифта заголока
|
||||||
|
var fontTitle = new Font();
|
||||||
|
fontTitle.Append(new Bold());
|
||||||
|
fontTitle.Append(new FontSize() { Val = 14D });
|
||||||
|
fontTitle.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color() { Theme = 1U });
|
||||||
|
fontTitle.Append(new FontName() { Val = "Times New Roman" });
|
||||||
|
fontTitle.Append(new FontFamilyNumbering() { Val = 2 });
|
||||||
|
fontTitle.Append(new FontScheme() { Val = FontSchemeValues.Minor });
|
||||||
|
|
||||||
|
// Добавление созданных шрифтов
|
||||||
|
fonts.Append(fontUsual);
|
||||||
|
fonts.Append(fontTitle);
|
||||||
|
|
||||||
|
// Создание заливки
|
||||||
|
var fills = new Fills() { Count = 2U };
|
||||||
|
|
||||||
|
var fill1 = new Fill();
|
||||||
|
fill1.Append(new PatternFill()
|
||||||
|
{
|
||||||
|
PatternType = PatternValues.None
|
||||||
|
});
|
||||||
|
|
||||||
|
var fill2 = new Fill();
|
||||||
|
fill2.Append(new PatternFill()
|
||||||
|
{
|
||||||
|
PatternType = PatternValues.Gray125
|
||||||
|
});
|
||||||
|
|
||||||
|
fills.Append(fill1);
|
||||||
|
fills.Append(fill2);
|
||||||
|
|
||||||
|
// Стиль границ ячейки - незакрашенный (для заголовка) и закрашенный
|
||||||
|
var borders = new Borders() { Count = 2U };
|
||||||
|
|
||||||
|
var borderNoBorder = new Border();
|
||||||
|
borderNoBorder.Append(new LeftBorder());
|
||||||
|
borderNoBorder.Append(new RightBorder());
|
||||||
|
borderNoBorder.Append(new TopBorder());
|
||||||
|
borderNoBorder.Append(new BottomBorder());
|
||||||
|
borderNoBorder.Append(new DiagonalBorder());
|
||||||
|
|
||||||
|
var borderThin = new Border();
|
||||||
|
|
||||||
|
var leftBorder = new LeftBorder() { Style = BorderStyleValues.Thin };
|
||||||
|
leftBorder.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color() { Indexed = 64U });
|
||||||
|
|
||||||
|
var rightBorder = new RightBorder()
|
||||||
|
{
|
||||||
|
Style = BorderStyleValues.Thin
|
||||||
|
};
|
||||||
|
rightBorder.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color() { Indexed = 64U });
|
||||||
|
|
||||||
|
var topBorder = new TopBorder() { Style = BorderStyleValues.Thin };
|
||||||
|
topBorder.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color() { Indexed = 64U });
|
||||||
|
|
||||||
|
var bottomBorder = new BottomBorder()
|
||||||
|
{
|
||||||
|
Style = BorderStyleValues.Thin
|
||||||
|
};
|
||||||
|
bottomBorder.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color() { Indexed = 64U });
|
||||||
|
|
||||||
|
borderThin.Append(leftBorder);
|
||||||
|
borderThin.Append(rightBorder);
|
||||||
|
borderThin.Append(topBorder);
|
||||||
|
borderThin.Append(bottomBorder);
|
||||||
|
borderThin.Append(new DiagonalBorder());
|
||||||
|
|
||||||
|
borders.Append(borderNoBorder);
|
||||||
|
borders.Append(borderThin);
|
||||||
|
|
||||||
|
// Формирование CellFormat из комбинаций шрифтов, заливок и т. д.
|
||||||
|
var cellStyleFormats = new CellStyleFormats() { Count = 1U };
|
||||||
|
|
||||||
|
var cellFormatStyle = new CellFormat()
|
||||||
|
{
|
||||||
|
NumberFormatId = 0U,
|
||||||
|
FontId = 0U,
|
||||||
|
FillId = 0U,
|
||||||
|
BorderId = 0U
|
||||||
|
};
|
||||||
|
|
||||||
|
cellStyleFormats.Append(cellFormatStyle);
|
||||||
|
|
||||||
|
var cellFormats = new CellFormats() { Count = 3U };
|
||||||
|
|
||||||
|
var cellFormatFont = new CellFormat()
|
||||||
|
{
|
||||||
|
NumberFormatId = 0U,
|
||||||
|
FontId = 0U,
|
||||||
|
FillId = 0U,
|
||||||
|
BorderId = 0U,
|
||||||
|
FormatId = 0U,
|
||||||
|
ApplyFont = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var cellFormatFontAndBorder = new CellFormat()
|
||||||
|
{
|
||||||
|
NumberFormatId = 0U,
|
||||||
|
FontId = 0U,
|
||||||
|
FillId = 0U,
|
||||||
|
BorderId = 1U,
|
||||||
|
FormatId = 0U,
|
||||||
|
ApplyFont = true,
|
||||||
|
ApplyBorder = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var cellFormatTitle = new CellFormat()
|
||||||
|
{
|
||||||
|
NumberFormatId = 0U,
|
||||||
|
FontId = 1U,
|
||||||
|
FillId = 0U,
|
||||||
|
BorderId = 0U,
|
||||||
|
FormatId = 0U,
|
||||||
|
Alignment = new Alignment()
|
||||||
|
{
|
||||||
|
Vertical = VerticalAlignmentValues.Center,
|
||||||
|
WrapText = true,
|
||||||
|
Horizontal = HorizontalAlignmentValues.Center
|
||||||
|
},
|
||||||
|
ApplyFont = true
|
||||||
|
};
|
||||||
|
|
||||||
|
// По итогу создали 3 стиля
|
||||||
|
cellFormats.Append(cellFormatFont);
|
||||||
|
cellFormats.Append(cellFormatFontAndBorder);
|
||||||
|
cellFormats.Append(cellFormatTitle);
|
||||||
|
|
||||||
|
var cellStyles = new CellStyles() { Count = 1U };
|
||||||
|
|
||||||
|
cellStyles.Append(new CellStyle()
|
||||||
|
{
|
||||||
|
Name = "Normal",
|
||||||
|
FormatId = 0U,
|
||||||
|
BuiltinId = 0U
|
||||||
|
});
|
||||||
|
|
||||||
|
var differentialFormats = new DocumentFormat.OpenXml.Office2013.Excel.DifferentialFormats() { Count = 0U };
|
||||||
|
|
||||||
|
var tableStyles = new TableStyles()
|
||||||
|
{
|
||||||
|
Count = 0U,
|
||||||
|
DefaultTableStyle = "TableStyleMedium2",
|
||||||
|
DefaultPivotStyle = "PivotStyleLight16"
|
||||||
|
};
|
||||||
|
|
||||||
|
var stylesheetExtensionList = new StylesheetExtensionList();
|
||||||
|
var stylesheetExtension1 = new StylesheetExtension()
|
||||||
|
{
|
||||||
|
Uri = "{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}"
|
||||||
|
};
|
||||||
|
|
||||||
|
stylesheetExtension1.AddNamespaceDeclaration("x14", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main");
|
||||||
|
stylesheetExtension1.Append(new SlicerStyles()
|
||||||
|
{
|
||||||
|
DefaultSlicerStyle = "SlicerStyleLight1"
|
||||||
|
});
|
||||||
|
|
||||||
|
var stylesheetExtension2 = new StylesheetExtension()
|
||||||
|
{
|
||||||
|
Uri = "{9260A510-F301-46a8-8635-F512D64BE5F5}"
|
||||||
|
};
|
||||||
|
|
||||||
|
stylesheetExtension2.AddNamespaceDeclaration("x15", "http://schemas.microsoft.com/office/spreadsheetml/2010/11/main");
|
||||||
|
|
||||||
|
stylesheetExtension2.Append(new TimelineStyles()
|
||||||
|
{
|
||||||
|
DefaultTimelineStyle = "TimeSlicerStyleLight1"
|
||||||
|
});
|
||||||
|
|
||||||
|
stylesheetExtensionList.Append(stylesheetExtension1);
|
||||||
|
stylesheetExtensionList.Append(stylesheetExtension2);
|
||||||
|
|
||||||
|
sp.Stylesheet.Append(fonts);
|
||||||
|
sp.Stylesheet.Append(fills);
|
||||||
|
sp.Stylesheet.Append(borders);
|
||||||
|
sp.Stylesheet.Append(cellStyleFormats);
|
||||||
|
sp.Stylesheet.Append(cellFormats);
|
||||||
|
sp.Stylesheet.Append(cellStyles);
|
||||||
|
sp.Stylesheet.Append(differentialFormats);
|
||||||
|
sp.Stylesheet.Append(tableStyles);
|
||||||
|
sp.Stylesheet.Append(stylesheetExtensionList);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получение номера стиля (одного из 3-х нами созданных) из типа
|
||||||
|
private static uint GetStyleValue(ExcelStyleInfoType styleInfo)
|
||||||
|
{
|
||||||
|
return styleInfo switch
|
||||||
|
{
|
||||||
|
ExcelStyleInfoType.Title => 2U,
|
||||||
|
ExcelStyleInfoType.TextWithBorder => 1U,
|
||||||
|
ExcelStyleInfoType.Text => 0U,
|
||||||
|
_ => 0U,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CreateExcel(ExcelInfo info)
|
||||||
|
{
|
||||||
|
// Создаём документ Excel
|
||||||
|
_spreadsheetDocument = SpreadsheetDocument.Create(info.FileName, SpreadsheetDocumentType.Workbook);
|
||||||
|
|
||||||
|
// Создаем книгу (в ней хранятся листы)
|
||||||
|
var workbookpart = _spreadsheetDocument.AddWorkbookPart();
|
||||||
|
|
||||||
|
workbookpart.Workbook = new Workbook();
|
||||||
|
|
||||||
|
CreateStyles(workbookpart);
|
||||||
|
|
||||||
|
// Получаем/создаем хранилище текстов для книги
|
||||||
|
_shareStringPart = _spreadsheetDocument.WorkbookPart!.GetPartsOfType<SharedStringTablePart>().Any()
|
||||||
|
? _spreadsheetDocument.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First()
|
||||||
|
: _spreadsheetDocument.WorkbookPart.AddNewPart<SharedStringTablePart>();
|
||||||
|
|
||||||
|
// Создаем SharedStringTable, если его нет
|
||||||
|
if (_shareStringPart.SharedStringTable == null)
|
||||||
|
{
|
||||||
|
_shareStringPart.SharedStringTable = new SharedStringTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создаем лист в книгу
|
||||||
|
var worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
|
||||||
|
worksheetPart.Worksheet = new Worksheet(new SheetData());
|
||||||
|
|
||||||
|
// Добавляем лист в книгу
|
||||||
|
var sheets = _spreadsheetDocument.WorkbookPart.Workbook.AppendChild(new Sheets());
|
||||||
|
|
||||||
|
var sheet = new Sheet()
|
||||||
|
{
|
||||||
|
Id = _spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart),
|
||||||
|
SheetId = 1,
|
||||||
|
Name = "Лист"
|
||||||
|
};
|
||||||
|
|
||||||
|
sheets.Append(sheet);
|
||||||
|
|
||||||
|
_worksheet = worksheetPart.Worksheet;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Метод вставки в лист книги
|
||||||
|
protected override void InsertCellInWorksheet(ExcelCellParameters excelParams)
|
||||||
|
{
|
||||||
|
if (_worksheet == null || _shareStringPart == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sheetData = _worksheet.GetFirstChild<SheetData>();
|
||||||
|
|
||||||
|
if (sheetData == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ищем строку, либо добавляем ее
|
||||||
|
Row row;
|
||||||
|
|
||||||
|
if (sheetData.Elements<Row>().Where(r => r.RowIndex! == excelParams.RowIndex).Any())
|
||||||
|
{
|
||||||
|
row = sheetData.Elements<Row>().Where(r => r.RowIndex! == excelParams.RowIndex).First();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
row = new Row() { RowIndex = excelParams.RowIndex };
|
||||||
|
sheetData.Append(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ищем нужную ячейку
|
||||||
|
Cell cell;
|
||||||
|
|
||||||
|
if (row.Elements<Cell>().Where(c => c.CellReference!.Value == excelParams.CellReference).Any())
|
||||||
|
{
|
||||||
|
cell = row.Elements<Cell>().Where(c => c.CellReference!.Value == excelParams.CellReference).First();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Все ячейки должны быть последовательно друг за другом расположены
|
||||||
|
// Нужно определить, после какой вставлять
|
||||||
|
Cell? refCell = null;
|
||||||
|
|
||||||
|
foreach (Cell rowCell in row.Elements<Cell>())
|
||||||
|
{
|
||||||
|
if (string.Compare(rowCell.CellReference!.Value, excelParams.CellReference, true) > 0)
|
||||||
|
{
|
||||||
|
refCell = rowCell;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var newCell = new Cell()
|
||||||
|
{
|
||||||
|
CellReference = excelParams.CellReference
|
||||||
|
};
|
||||||
|
|
||||||
|
row.InsertBefore(newCell, refCell);
|
||||||
|
cell = newCell;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вставляем новый текст
|
||||||
|
_shareStringPart.SharedStringTable.AppendChild(new SharedStringItem(new Text(excelParams.Text)));
|
||||||
|
_shareStringPart.SharedStringTable.Save();
|
||||||
|
|
||||||
|
cell.CellValue = new CellValue((_shareStringPart.SharedStringTable.Elements<SharedStringItem>().Count() - 1).ToString());
|
||||||
|
cell.DataType = new EnumValue<CellValues>(CellValues.SharedString);
|
||||||
|
cell.StyleIndex = GetStyleValue(excelParams.StyleInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Метод объединения ячеек
|
||||||
|
protected override void MergeCells(ExcelMergeParameters excelParams)
|
||||||
|
{
|
||||||
|
if (_worksheet == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MergeCells mergeCells;
|
||||||
|
|
||||||
|
if (_worksheet.Elements<MergeCells>().Any())
|
||||||
|
{
|
||||||
|
mergeCells = _worksheet.Elements<MergeCells>().First();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mergeCells = new MergeCells();
|
||||||
|
|
||||||
|
if (_worksheet.Elements<CustomSheetView>().Any())
|
||||||
|
{
|
||||||
|
_worksheet.InsertAfter(mergeCells, _worksheet.Elements<CustomSheetView>().First());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_worksheet.InsertAfter(mergeCells, _worksheet.Elements<SheetData>().First());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var mergeCell = new MergeCell()
|
||||||
|
{
|
||||||
|
Reference = new StringValue(excelParams.Merge)
|
||||||
|
};
|
||||||
|
|
||||||
|
mergeCells.Append(mergeCell);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void SaveExcel(ExcelInfo info)
|
||||||
|
{
|
||||||
|
if (_spreadsheetDocument == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_spreadsheetDocument.WorkbookPart!.Workbook.Save();
|
||||||
|
_spreadsheetDocument.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,131 @@
|
|||||||
|
using FurnitureAssemblyBusinessLogic.OfficePackage.HelperEnums;
|
||||||
|
using FurnitureAssemblyBusinessLogic.OfficePackage.HelperModels;
|
||||||
|
using MigraDoc.DocumentObjectModel;
|
||||||
|
using MigraDoc.DocumentObjectModel.Tables;
|
||||||
|
using MigraDoc.Rendering;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.OfficePackage.Implements
|
||||||
|
{
|
||||||
|
// Реализация абстрактного класса создания Pdf документа
|
||||||
|
public class SaveToPdf : AbstractSaveToPdf
|
||||||
|
{
|
||||||
|
private Document? _document;
|
||||||
|
|
||||||
|
private Section? _section;
|
||||||
|
|
||||||
|
private Table? _table;
|
||||||
|
|
||||||
|
// Преобразование необходимого типа выравнивания в соотвествующее выравнивание в MigraDoc
|
||||||
|
private static ParagraphAlignment GetParagraphAlignment(PdfParagraphAlignmentType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
PdfParagraphAlignmentType.Center => ParagraphAlignment.Center,
|
||||||
|
PdfParagraphAlignmentType.Left => ParagraphAlignment.Left,
|
||||||
|
PdfParagraphAlignmentType.Right => ParagraphAlignment.Right,
|
||||||
|
_ => ParagraphAlignment.Justify,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создание стилей для документа
|
||||||
|
private static void DefineStyles(Document document)
|
||||||
|
{
|
||||||
|
var style = document.Styles["Normal"];
|
||||||
|
|
||||||
|
style.Font.Name = "Times New Roman";
|
||||||
|
style.Font.Size = 14;
|
||||||
|
|
||||||
|
style = document.Styles.AddStyle("NormalTitle", "Normal");
|
||||||
|
style.Font.Bold = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CreatePdf(PdfInfo info)
|
||||||
|
{
|
||||||
|
// Создаём документ
|
||||||
|
_document = new Document();
|
||||||
|
|
||||||
|
// Передаём для него стили
|
||||||
|
DefineStyles(_document);
|
||||||
|
|
||||||
|
// Получение первой секции документа
|
||||||
|
_section = _document.AddSection();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CreateParagraph(PdfParagraph pdfParagraph)
|
||||||
|
{
|
||||||
|
if (_section == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var paragraph = _section.AddParagraph(pdfParagraph.Text);
|
||||||
|
paragraph.Format.SpaceAfter = "1cm";
|
||||||
|
paragraph.Format.Alignment = GetParagraphAlignment(pdfParagraph.ParagraphAlignment);
|
||||||
|
paragraph.Style = pdfParagraph.Style;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CreateTable(List<string> columns)
|
||||||
|
{
|
||||||
|
if (_document == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем таблицу в документ как последнюю секцию (?)
|
||||||
|
_table = _document.LastSection.AddTable();
|
||||||
|
|
||||||
|
foreach (var elem in columns)
|
||||||
|
{
|
||||||
|
_table.AddColumn(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CreateRow(PdfRowParameters rowParameters)
|
||||||
|
{
|
||||||
|
if (_table == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавление строки в таблицу
|
||||||
|
var row = _table.AddRow();
|
||||||
|
|
||||||
|
for (int i = 0; i < rowParameters.Texts.Count; ++i)
|
||||||
|
{
|
||||||
|
// Ячейка добавляется добавлением параграфа
|
||||||
|
row.Cells[i].AddParagraph(rowParameters.Texts[i]);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(rowParameters.Style))
|
||||||
|
{
|
||||||
|
row.Cells[i].Style = rowParameters.Style;
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit borderWidth = 0.5;
|
||||||
|
|
||||||
|
row.Cells[i].Borders.Left.Width = borderWidth;
|
||||||
|
row.Cells[i].Borders.Right.Width = borderWidth;
|
||||||
|
row.Cells[i].Borders.Top.Width = borderWidth;
|
||||||
|
row.Cells[i].Borders.Bottom.Width = borderWidth;
|
||||||
|
|
||||||
|
row.Cells[i].Format.Alignment = GetParagraphAlignment(rowParameters.ParagraphAlignment);
|
||||||
|
row.Cells[i].VerticalAlignment = VerticalAlignment.Center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void SavePdf(PdfInfo info)
|
||||||
|
{
|
||||||
|
var renderer = new PdfDocumentRenderer(true)
|
||||||
|
{
|
||||||
|
Document = _document
|
||||||
|
};
|
||||||
|
|
||||||
|
renderer.RenderDocument();
|
||||||
|
renderer.PdfDocument.Save(info.FileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,159 @@
|
|||||||
|
using DocumentFormat.OpenXml.Packaging;
|
||||||
|
using DocumentFormat.OpenXml.Wordprocessing;
|
||||||
|
using DocumentFormat.OpenXml;
|
||||||
|
using FurnitureAssemblyBusinessLogic.OfficePackage.HelperEnums;
|
||||||
|
using FurnitureAssemblyBusinessLogic.OfficePackage.HelperModels;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyBusinessLogic.OfficePackage.Implements
|
||||||
|
{
|
||||||
|
// Реализация абстрактного класса сохранения в Word документ
|
||||||
|
public class SaveToWord : AbstractSaveToWord
|
||||||
|
{
|
||||||
|
private WordprocessingDocument? _wordDocument;
|
||||||
|
|
||||||
|
private Body? _docBody;
|
||||||
|
|
||||||
|
// Получение типа выравнивания
|
||||||
|
private static JustificationValues GetJustificationValues(WordJustificationType type)
|
||||||
|
{
|
||||||
|
// Выравнивание слева будет в том случае, если передаётся неизвестный тип выравнивания
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
WordJustificationType.Both => JustificationValues.Both,
|
||||||
|
WordJustificationType.Center => JustificationValues.Center,
|
||||||
|
_ => JustificationValues.Left,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Настройки страницы
|
||||||
|
private static SectionProperties CreateSectionProperties()
|
||||||
|
{
|
||||||
|
var properties = new SectionProperties();
|
||||||
|
|
||||||
|
// Прописываем портретную ориентацию
|
||||||
|
var pageSize = new PageSize
|
||||||
|
{
|
||||||
|
Orient = PageOrientationValues.Portrait
|
||||||
|
};
|
||||||
|
|
||||||
|
properties.AppendChild(pageSize);
|
||||||
|
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Задание форматирования для абзаца
|
||||||
|
private static ParagraphProperties? CreateParagraphProperties(WordTextProperties? paragraphProperties)
|
||||||
|
{
|
||||||
|
if (paragraphProperties == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var properties = new ParagraphProperties();
|
||||||
|
|
||||||
|
// Вытаскиваем выравнивание текста
|
||||||
|
properties.AppendChild(new Justification()
|
||||||
|
{
|
||||||
|
Val = GetJustificationValues(paragraphProperties.JustificationType)
|
||||||
|
});
|
||||||
|
|
||||||
|
properties.AppendChild(new SpacingBetweenLines
|
||||||
|
{
|
||||||
|
LineRule = LineSpacingRuleValues.Auto
|
||||||
|
});
|
||||||
|
|
||||||
|
properties.AppendChild(new Indentation());
|
||||||
|
|
||||||
|
var paragraphMarkRunProperties = new ParagraphMarkRunProperties();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(paragraphProperties.Size))
|
||||||
|
{
|
||||||
|
paragraphMarkRunProperties.AppendChild(new FontSize
|
||||||
|
{
|
||||||
|
Val = paragraphProperties.Size
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
properties.AppendChild(paragraphMarkRunProperties);
|
||||||
|
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CreateWord(WordInfo info)
|
||||||
|
{
|
||||||
|
// Создаём документ Word
|
||||||
|
_wordDocument = WordprocessingDocument.Create(info.FileName, WordprocessingDocumentType.Document);
|
||||||
|
|
||||||
|
// Вытаскиваем главную часть из вордовского документа
|
||||||
|
MainDocumentPart mainPart = _wordDocument.AddMainDocumentPart();
|
||||||
|
|
||||||
|
mainPart.Document = new Document();
|
||||||
|
|
||||||
|
// Генерируем тело основной части документа
|
||||||
|
_docBody = mainPart.Document.AppendChild(new Body());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CreateParagraph(WordParagraph paragraph)
|
||||||
|
{
|
||||||
|
// Проверка на то, был ли вызван WordprocessingDocument.Create (создался ли документ) и есть ли вообще параграф для вставки
|
||||||
|
if (_docBody == null || paragraph == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var docParagraph = new Paragraph();
|
||||||
|
|
||||||
|
// Добавляем свойства параграфа
|
||||||
|
docParagraph.AppendChild(CreateParagraphProperties(paragraph.TextProperties));
|
||||||
|
|
||||||
|
// Вставляем блоки текста (их называют Run)
|
||||||
|
foreach (var run in paragraph.Texts)
|
||||||
|
{
|
||||||
|
var docRun = new Run();
|
||||||
|
var properties = new RunProperties();
|
||||||
|
|
||||||
|
// Задание свойств текста - размер и жирность
|
||||||
|
properties.AppendChild(new FontSize { Val = run.Item2.Size });
|
||||||
|
|
||||||
|
if (run.Item2.Bold)
|
||||||
|
{
|
||||||
|
properties.AppendChild(new Bold());
|
||||||
|
}
|
||||||
|
|
||||||
|
docRun.AppendChild(properties);
|
||||||
|
|
||||||
|
docRun.AppendChild(new Text
|
||||||
|
{
|
||||||
|
Text = run.Item1,
|
||||||
|
Space = SpaceProcessingModeValues.Preserve
|
||||||
|
});
|
||||||
|
|
||||||
|
docParagraph.AppendChild(docRun);
|
||||||
|
}
|
||||||
|
|
||||||
|
_docBody.AppendChild(docParagraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Метод сохранения документа
|
||||||
|
protected override void SaveWord(WordInfo info)
|
||||||
|
{
|
||||||
|
if (_docBody == null || _wordDocument == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вставляем информацию по секциям (смотри, что является входным параметром)
|
||||||
|
_docBody.AppendChild(CreateSectionProperties());
|
||||||
|
|
||||||
|
// Сохраняем документ
|
||||||
|
_wordDocument.MainDocumentPart!.Document.Save();
|
||||||
|
|
||||||
|
_wordDocument.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
FurnitureAssembly/FurnitureAssemblyClientApp/APIClient.cs
Normal file
52
FurnitureAssembly/FurnitureAssemblyClientApp/APIClient.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
using FurnitureAssemblyContracts.ViewModels;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyClientApp
|
||||||
|
{
|
||||||
|
public class APIClient
|
||||||
|
{
|
||||||
|
private static readonly HttpClient _client = new();
|
||||||
|
|
||||||
|
public static ClientViewModel? Client { get; set; } = null;
|
||||||
|
|
||||||
|
public static void Connect(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
_client.BaseAddress = new Uri(configuration["IPAddress"]);
|
||||||
|
_client.DefaultRequestHeaders.Accept.Clear();
|
||||||
|
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T? GetRequest<T>(string requestUrl)
|
||||||
|
{
|
||||||
|
var response = _client.GetAsync(requestUrl);
|
||||||
|
var result = response.Result.Content.ReadAsStringAsync().Result;
|
||||||
|
|
||||||
|
if (response.Result.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return JsonConvert.DeserializeObject<T>(result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PostRequest<T>(string requestUrl, T model)
|
||||||
|
{
|
||||||
|
var json = JsonConvert.SerializeObject(model);
|
||||||
|
var data = new StringContent(json, Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
var response = _client.PostAsync(requestUrl, data);
|
||||||
|
|
||||||
|
var result = response.Result.Content.ReadAsStringAsync().Result;
|
||||||
|
|
||||||
|
if (!response.Result.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
throw new Exception(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,188 @@
|
|||||||
|
using FurnitureAssemblyClientApp.Models;
|
||||||
|
using FurnitureAssemblyContracts.BindingModels;
|
||||||
|
using FurnitureAssemblyContracts.ViewModels;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace FurnitureAssemblyClientApp.Controllers
|
||||||
|
{
|
||||||
|
public class HomeController : Controller
|
||||||
|
{
|
||||||
|
private readonly ILogger<HomeController> _logger;
|
||||||
|
|
||||||
|
public HomeController(ILogger<HomeController> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Через API клиента Get-запросом получается список его собственных заказов
|
||||||
|
public IActionResult Index()
|
||||||
|
{
|
||||||
|
if (APIClient.Client == null)
|
||||||
|
{
|
||||||
|
return Redirect("~/Home/Enter");
|
||||||
|
}
|
||||||
|
return View(APIClient.GetRequest<List<OrderViewModel>>($"api/main/getorders?clientId={APIClient.Client.Id}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получение данных Get-ом
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult Privacy()
|
||||||
|
{
|
||||||
|
if (APIClient.Client == null)
|
||||||
|
{
|
||||||
|
return Redirect("~/Home/Enter");
|
||||||
|
}
|
||||||
|
return View(APIClient.Client);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Изменение данных Post-ом
|
||||||
|
[HttpPost]
|
||||||
|
public void Privacy(string login, string password, string fio)
|
||||||
|
{
|
||||||
|
if (APIClient.Client == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Вы как сюда попали? Суда вход только авторизованным. Кыш-кыш");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(login) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(fio))
|
||||||
|
{
|
||||||
|
throw new Exception("Введите логин, пароль и ФИО");
|
||||||
|
}
|
||||||
|
|
||||||
|
APIClient.PostRequest("api/client/updatedata", new ClientBindingModel
|
||||||
|
{
|
||||||
|
Id = APIClient.Client.Id,
|
||||||
|
ClientFIO = fio,
|
||||||
|
Email = login,
|
||||||
|
Password = password
|
||||||
|
});
|
||||||
|
|
||||||
|
APIClient.Client.ClientFIO = fio;
|
||||||
|
APIClient.Client.Email = login;
|
||||||
|
APIClient.Client.Password = password;
|
||||||
|
|
||||||
|
Response.Redirect("Index");
|
||||||
|
}
|
||||||
|
|
||||||
|
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||||
|
public IActionResult Error()
|
||||||
|
{
|
||||||
|
return View(new ErrorViewModel
|
||||||
|
{
|
||||||
|
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Открытие Vieхи
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult Enter()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отправляем указанные данные на проверку
|
||||||
|
[HttpPost]
|
||||||
|
public void Enter(string login, string password)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(login) || string.IsNullOrEmpty(password))
|
||||||
|
{
|
||||||
|
throw new Exception("Введите логин и пароль");
|
||||||
|
}
|
||||||
|
|
||||||
|
APIClient.Client = APIClient.GetRequest<ClientViewModel>($"api/client/login?login={login}&password={password}");
|
||||||
|
|
||||||
|
if (APIClient.Client == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Неверный логин/пароль");
|
||||||
|
}
|
||||||
|
|
||||||
|
Response.Redirect("Index");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Открытие Vieхи
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult Register()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post-запрос по созданию нового пользователя
|
||||||
|
[HttpPost]
|
||||||
|
public void Register(string login, string password, string fio)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(login) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(fio))
|
||||||
|
{
|
||||||
|
throw new Exception("Введите логин, пароль и ФИО");
|
||||||
|
}
|
||||||
|
|
||||||
|
APIClient.PostRequest("api/client/register", new ClientBindingModel
|
||||||
|
{
|
||||||
|
ClientFIO = fio,
|
||||||
|
Email = login,
|
||||||
|
Password = password
|
||||||
|
});
|
||||||
|
|
||||||
|
// Переход на вкладку "Enter", чтобы пользователь сразу смог зайти
|
||||||
|
Response.Redirect("Enter");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создание заказа. Получаем и передаём список изделий во View
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult Create()
|
||||||
|
{
|
||||||
|
ViewBag.Furnitures = APIClient.GetRequest<List<FurnitureViewModel>>("api/main/getfurniturelist");
|
||||||
|
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создание заказа Post-запросом
|
||||||
|
[HttpPost]
|
||||||
|
public void Create(int furniture, int count)
|
||||||
|
{
|
||||||
|
if (APIClient.Client == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным. Кыш-кыш");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count <= 0)
|
||||||
|
{
|
||||||
|
throw new Exception("Количество и сумма должны быть больше 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
APIClient.PostRequest("api/main/createorder", new OrderBindingModel
|
||||||
|
{
|
||||||
|
ClientId = APIClient.Client.Id,
|
||||||
|
FurnitureId = furniture,
|
||||||
|
Count = count,
|
||||||
|
Sum = Calc(count, furniture)
|
||||||
|
});
|
||||||
|
|
||||||
|
Response.Redirect("Index");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Подсчёт стоимости заказа
|
||||||
|
[HttpPost]
|
||||||
|
public double Calc(int count, int furniture)
|
||||||
|
{
|
||||||
|
var furnitures = APIClient.GetRequest<FurnitureViewModel>($"api/main/getfurniture?furnitureId={furniture}");
|
||||||
|
|
||||||
|
return count * (furnitures?.Price ?? 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Для работы с письмами
|
||||||
|
[HttpGet]
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult Mails()
|
||||||
|
{
|
||||||
|
if (APIClient.Client == null)
|
||||||
|
{
|
||||||
|
return Redirect("~/Home/Enter");
|
||||||
|
}
|
||||||
|
|
||||||
|
return View(APIClient.GetRequest<List<MessageInfoViewModel>>($"api/client/getmessages?clientId={APIClient.Client.Id}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Remove="Views\Home\_Layout.cshtml" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\FurnitureAssemblyContracts\FurnitureAssemblyContracts.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<UpToDateCheckInput Remove="Views\Home\_Layout.cshtml" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<_ContentIncludedByDefault Remove="Views\Home\_Layout.cshtml" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="Views\Home\_Layout.cshtml" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -0,0 +1,9 @@
|
|||||||
|
namespace FurnitureAssemblyClientApp.Models
|
||||||
|
{
|
||||||
|
public class ErrorViewModel
|
||||||
|
{
|
||||||
|
public string? RequestId { get; set; }
|
||||||
|
|
||||||
|
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||||
|
}
|
||||||
|
}
|
30
FurnitureAssembly/FurnitureAssemblyClientApp/Program.cs
Normal file
30
FurnitureAssembly/FurnitureAssemblyClientApp/Program.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using FurnitureAssemblyClientApp;
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
// Add services to the container.
|
||||||
|
builder.Services.AddControllersWithViews();
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
APIClient.Connect(builder.Configuration);
|
||||||
|
|
||||||
|
// Configure the HTTP request pipeline.
|
||||||
|
if (!app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseExceptionHandler("/Home/Error");
|
||||||
|
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||||
|
app.UseHsts();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
app.UseStaticFiles();
|
||||||
|
|
||||||
|
app.UseRouting();
|
||||||
|
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.MapControllerRoute(
|
||||||
|
name: "default",
|
||||||
|
pattern: "{controller=Home}/{action=Index}/{id?}");
|
||||||
|
|
||||||
|
app.Run();
|
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:59437",
|
||||||
|
"sslPort": 44389
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"FurnitureAssemblyClientApp": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"applicationUrl": "https://localhost:7271;http://localhost:5271",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
@{
|
||||||
|
ViewData["Title"] = "Create";
|
||||||
|
}
|
||||||
|
<div class="text-center">
|
||||||
|
<h2 class="display-4">Создание заказа</h2>
|
||||||
|
</div>
|
||||||
|
<form method="post">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">Мебель:</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<select id="furniture" name="furniture" class="form-control" asp-items="@(new SelectList(@ViewBag.Furnitures,"Id", "FurnitureName"))"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">Количество:</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<input type="text" name="count" id="count" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">Сумма:</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<input type="text" id="sum" name="sum" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-8"></div>
|
||||||
|
<div class="col-4">
|
||||||
|
<input type="submit" value="Создать" class="btn btn-primary" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<script>
|
||||||
|
$('#furniture').on('change', function () {
|
||||||
|
check();
|
||||||
|
});
|
||||||
|
$('#count').on('change', function () {
|
||||||
|
check();
|
||||||
|
});
|
||||||
|
function check() {
|
||||||
|
var count = $('#count').val();
|
||||||
|
var furniture = $('#furniture').val();
|
||||||
|
if (count && furniture) {
|
||||||
|
$.ajax({
|
||||||
|
method: "POST",
|
||||||
|
url: "/Home/Calc",
|
||||||
|
data: { count: count, furniture: furniture },
|
||||||
|
success: function (result) {
|
||||||
|
$("#sum").val(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,20 @@
|
|||||||
|
@{
|
||||||
|
ViewData["Title"] = "Enter";
|
||||||
|
}
|
||||||
|
<div class="text-center">
|
||||||
|
<h2 class="display-4">Вход в приложение</h2>
|
||||||
|
</div>
|
||||||
|
<form method="post">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">Логин:</div>
|
||||||
|
<div class="col-8"><input type="text" name="login" /></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">Пароль:</div>
|
||||||
|
<div class="col-8"><input type="password" name="password" /></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-8"></div>
|
||||||
|
<div class="col-4"><input type="submit" value="Ввод" class="btn btnprimary" /></div>
|
||||||
|
</div>
|
||||||
|
</form>
|
@ -0,0 +1,69 @@
|
|||||||
|
@using FurnitureAssemblyContracts.ViewModels
|
||||||
|
@model List<OrderViewModel>
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Home Page";
|
||||||
|
}
|
||||||
|
<div class="text-center">
|
||||||
|
<h1 class="display-4">Заказы</h1>
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
@{
|
||||||
|
if (Model == null)
|
||||||
|
{
|
||||||
|
<h3 class="display-4">Авторизоваться!</h3>
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
<p>
|
||||||
|
<a asp-action="Create">Создать заказ</a>
|
||||||
|
</p>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Номер заказа
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Мебель
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Дата создания
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Количество
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Сумма
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Статус
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (var item in Model)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
@Html.DisplayFor(modelItem => item.Id)
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
@Html.DisplayFor(modelItem => item.FurnitureName)
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
@Html.DisplayFor(modelItem => item.DateCreate)
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
@Html.DisplayFor(modelItem => item.Count)
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
@Html.DisplayFor(modelItem => item.Sum)
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
@Html.DisplayFor(modelItem => item.Status)
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
</div>
|
@ -0,0 +1,51 @@
|
|||||||
|
@using FurnitureAssemblyContracts.ViewModels
|
||||||
|
|
||||||
|
@model List<MessageInfoViewModel>
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Mails";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="text-center">
|
||||||
|
<h1 class="display-4">Заказы</h1>
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
@{
|
||||||
|
if (Model == null)
|
||||||
|
{
|
||||||
|
<h3 class="display-4">Авторизируйтесь</h3>
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Дата письма
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Заголовок
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Текст
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (var item in Model)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
@Html.DisplayFor(modelItem => item.DateDelivery)
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
@Html.DisplayFor(modelItem => item.Subject)
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
@Html.DisplayFor(modelItem => item.Body)
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
</div>
|
@ -0,0 +1,35 @@
|
|||||||
|
@using FurnitureAssemblyContracts.ViewModels
|
||||||
|
|
||||||
|
@model ClientViewModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Privacy Policy";
|
||||||
|
}
|
||||||
|
<div class="text-center">
|
||||||
|
<h2 class="display-4">Личные данные</h2>
|
||||||
|
</div>
|
||||||
|
<form method="post">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">Логин:</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<input type="text" name="login" value="@Model.Email" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">Пароль:</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<input type="password" name="password" value="@Model.Password" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">ФИО:</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<input type="text" name="fio" value="@Model.ClientFIO" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-8"></div>
|
||||||
|
<div class="col-4">
|
||||||
|
<input type="submit" value="Сохранить" class="btn btn-primary" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
@ -0,0 +1,26 @@
|
|||||||
|
@{
|
||||||
|
ViewData["Title"] = "Register";
|
||||||
|
}
|
||||||
|
<div class="text-center">
|
||||||
|
<h2 class="display-4">Регистрация</h2>
|
||||||
|
</div>
|
||||||
|
<form method="post">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">Логин:</div>
|
||||||
|
<div class="col-8"><input type="text" name="login" /></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">Пароль:</div>
|
||||||
|
<div class="col-8"><input type="password" name="password" /></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">ФИО:</div><div class="col-8"><input type="text" name="fio" /></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-8"></div>
|
||||||
|
<div class="col-4">
|
||||||
|
<input type="submit" value="Зарегистрировать"
|
||||||
|
class="btn btn-primary" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>@ViewData["Title"] - FurnitureAssemblyClientApp</title>
|
||||||
|
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
||||||
|
<link rel="stylesheet" href="~/css/site.css" />
|
||||||
|
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||||
|
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bgwhite border-bottom box-shadow mb-3">
|
||||||
|
<div class="container">
|
||||||
|
<a class="navbar-brand" asp-area="" asp-controller="Home" aspaction="Index">Сборка мебели</a>
|
||||||
|
<button class="navbar-toggler" type="button" datatoggle="collapse" data-target=".navbar-collapse" ariacontrols="navbarSupportedContent"
|
||||||
|
aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="navbar-collapse collapse d-sm-inline-flex flex-smrow-reverse">
|
||||||
|
<ul class="navbar-nav flex-grow-1">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link text-dark" asparea="" asp-controller="Home" asp-action="Index">Заказы</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link text-dark" asparea="" asp-controller="Home" asp-action="Privacy">Личные данные</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link text-dark" asparea="" asp-controller="Home" asp-action="Mails">Письма</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link text-dark" asparea="" asp-controller="Home" asp-action="Enter">Вход</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link text-dark" asparea="" asp-controller="Home" asp-action="Register">Регистрация</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<div class="container">
|
||||||
|
<main role="main" class="pb-3">
|
||||||
|
@RenderBody()
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
<footer class="border-top footer text-muted">
|
||||||
|
<div class="container">
|
||||||
|
© 2024 - СборкаМебели - <a asp-area="" aspcontroller="Home" asp-action="Privacy">Личные данные</a>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||||
|
@RenderSection("Scripts", required: false)
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,25 @@
|
|||||||
|
@model ErrorViewModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Error";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h1 class="text-danger">Error.</h1>
|
||||||
|
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||||
|
|
||||||
|
@if (Model.ShowRequestId)
|
||||||
|
{
|
||||||
|
<p>
|
||||||
|
<strong>Request ID:</strong> <code>@Model.RequestId</code>
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
<h3>Development Mode</h3>
|
||||||
|
<p>
|
||||||
|
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||||
|
It can result in displaying sensitive information from exceptions to end users.
|
||||||
|
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||||
|
and restarting the app.
|
||||||
|
</p>
|
@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>@ViewData["Title"] - FurnitureAssemblyClientApp</title>
|
||||||
|
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
||||||
|
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
||||||
|
<link rel="stylesheet" href="~/FurnitureAssemblyClientApp.styles.css" asp-append-version="true" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">FurnitureAssemblyClientApp</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
|
||||||
|
aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
|
||||||
|
<ul class="navbar-nav flex-grow-1">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<div class="container">
|
||||||
|
<main role="main" class="pb-3">
|
||||||
|
@RenderBody()
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="border-top footer text-muted">
|
||||||
|
<div class="container">
|
||||||
|
© 2024 - FurnitureAssemblyClientApp - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||||
|
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||||
|
@await RenderSectionAsync("Scripts", required: false)
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,48 @@
|
|||||||
|
/* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
||||||
|
for details on configuring this project to bundle and minify static web assets. */
|
||||||
|
|
||||||
|
a.navbar-brand {
|
||||||
|
white-space: normal;
|
||||||
|
text-align: center;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #0077cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #1b6ec2;
|
||||||
|
border-color: #1861ac;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #1b6ec2;
|
||||||
|
border-color: #1861ac;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-top {
|
||||||
|
border-top: 1px solid #e5e5e5;
|
||||||
|
}
|
||||||
|
.border-bottom {
|
||||||
|
border-bottom: 1px solid #e5e5e5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-shadow {
|
||||||
|
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.accept-policy {
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 60px;
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
|
||||||
|
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
|
@ -0,0 +1,3 @@
|
|||||||
|
@using FurnitureAssemblyClientApp
|
||||||
|
@using FurnitureAssemblyClientApp.Models
|
||||||
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
@ -0,0 +1,3 @@
|
|||||||
|
@{
|
||||||
|
Layout = "_Layout";
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*",
|
||||||
|
"IPAddress": "http://localhost:5266/"
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
html {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
html {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
position: relative;
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin-bottom: 60px;
|
||||||
|
}
|
BIN
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/favicon.ico
Normal file
BIN
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
@ -0,0 +1,4 @@
|
|||||||
|
// Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
||||||
|
// for details on configuring this project to bundle and minify static web assets.
|
||||||
|
|
||||||
|
// Write your JavaScript code.
|
@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2011-2021 Twitter, Inc.
|
||||||
|
Copyright (c) 2011-2021 The Bootstrap Authors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
4997
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css
vendored
Normal file
4997
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4996
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css
vendored
Normal file
4996
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
427
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css
vendored
Normal file
427
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css
vendored
Normal file
@ -0,0 +1,427 @@
|
|||||||
|
/*!
|
||||||
|
* Bootstrap Reboot v5.1.0 (https://getbootstrap.com/)
|
||||||
|
* Copyright 2011-2021 The Bootstrap Authors
|
||||||
|
* Copyright 2011-2021 Twitter, Inc.
|
||||||
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||||
|
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||||
|
*/
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
:root {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: var(--bs-body-font-family);
|
||||||
|
font-size: var(--bs-body-font-size);
|
||||||
|
font-weight: var(--bs-body-font-weight);
|
||||||
|
line-height: var(--bs-body-line-height);
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
text-align: var(--bs-body-text-align);
|
||||||
|
background-color: var(--bs-body-bg);
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 1rem 0;
|
||||||
|
color: inherit;
|
||||||
|
background-color: currentColor;
|
||||||
|
border: 0;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr:not([size]) {
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6, h5, h4, h3, h2, h1 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: calc(1.375rem + 1.5vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: calc(1.325rem + 0.9vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h2 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: calc(1.3rem + 0.6vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h3 {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h4 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
abbr[title],
|
||||||
|
abbr[data-bs-original-title] {
|
||||||
|
-webkit-text-decoration: underline dotted;
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
cursor: help;
|
||||||
|
-webkit-text-decoration-skip-ink: none;
|
||||||
|
text-decoration-skip-ink: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
address {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-style: normal;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul {
|
||||||
|
padding-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul,
|
||||||
|
dl {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol ol,
|
||||||
|
ul ul,
|
||||||
|
ol ul,
|
||||||
|
ul ol {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark {
|
||||||
|
padding: 0.2em;
|
||||||
|
background-color: #fcf8e3;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
position: relative;
|
||||||
|
font-size: 0.75em;
|
||||||
|
line-height: 0;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #0d6efd;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: #0a58ca;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre,
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
|
font-size: 1em;
|
||||||
|
direction: ltr /* rtl:ignore */;
|
||||||
|
unicode-bidi: bidi-override;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
display: block;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
overflow: auto;
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
pre code {
|
||||||
|
font-size: inherit;
|
||||||
|
color: inherit;
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-size: 0.875em;
|
||||||
|
color: #d63384;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
a > code {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
kbd {
|
||||||
|
padding: 0.2rem 0.4rem;
|
||||||
|
font-size: 0.875em;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #212529;
|
||||||
|
border-radius: 0.2rem;
|
||||||
|
}
|
||||||
|
kbd kbd {
|
||||||
|
padding: 0;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
svg {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
caption-side: bottom;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
caption {
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
color: #6c757d;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: inherit;
|
||||||
|
text-align: -webkit-match-parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead,
|
||||||
|
tbody,
|
||||||
|
tfoot,
|
||||||
|
tr,
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border-color: inherit;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus:not(:focus-visible) {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
button,
|
||||||
|
select,
|
||||||
|
optgroup,
|
||||||
|
textarea {
|
||||||
|
margin: 0;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[role=button] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
word-wrap: normal;
|
||||||
|
}
|
||||||
|
select:disabled {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[list]::-webkit-calendar-picker-indicator {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type=button],
|
||||||
|
[type=reset],
|
||||||
|
[type=submit] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
button:not(:disabled),
|
||||||
|
[type=button]:not(:disabled),
|
||||||
|
[type=reset]:not(:disabled),
|
||||||
|
[type=submit]:not(:disabled) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-moz-focus-inner {
|
||||||
|
padding: 0;
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
min-width: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
legend {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
legend + * {
|
||||||
|
clear: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-datetime-edit-fields-wrapper,
|
||||||
|
::-webkit-datetime-edit-text,
|
||||||
|
::-webkit-datetime-edit-minute,
|
||||||
|
::-webkit-datetime-edit-hour-field,
|
||||||
|
::-webkit-datetime-edit-day-field,
|
||||||
|
::-webkit-datetime-edit-month-field,
|
||||||
|
::-webkit-datetime-edit-year-field {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-inner-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type=search] {
|
||||||
|
outline-offset: -2px;
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* rtl:raw:
|
||||||
|
[type="tel"],
|
||||||
|
[type="url"],
|
||||||
|
[type="email"],
|
||||||
|
[type="number"] {
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-color-swatch-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::file-selector-button {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
font: inherit;
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,8 @@
|
|||||||
|
/*!
|
||||||
|
* Bootstrap Reboot v5.1.0 (https://getbootstrap.com/)
|
||||||
|
* Copyright 2011-2021 The Bootstrap Authors
|
||||||
|
* Copyright 2011-2021 Twitter, Inc.
|
||||||
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||||
|
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||||
|
*/*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}
|
||||||
|
/*# sourceMappingURL=bootstrap-reboot.min.css.map */
|
File diff suppressed because one or more lines are too long
424
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css
vendored
Normal file
424
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css
vendored
Normal file
@ -0,0 +1,424 @@
|
|||||||
|
/*!
|
||||||
|
* Bootstrap Reboot v5.1.0 (https://getbootstrap.com/)
|
||||||
|
* Copyright 2011-2021 The Bootstrap Authors
|
||||||
|
* Copyright 2011-2021 Twitter, Inc.
|
||||||
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||||
|
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||||
|
*/
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
:root {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: var(--bs-body-font-family);
|
||||||
|
font-size: var(--bs-body-font-size);
|
||||||
|
font-weight: var(--bs-body-font-weight);
|
||||||
|
line-height: var(--bs-body-line-height);
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
text-align: var(--bs-body-text-align);
|
||||||
|
background-color: var(--bs-body-bg);
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 1rem 0;
|
||||||
|
color: inherit;
|
||||||
|
background-color: currentColor;
|
||||||
|
border: 0;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr:not([size]) {
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6, h5, h4, h3, h2, h1 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: calc(1.375rem + 1.5vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: calc(1.325rem + 0.9vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h2 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: calc(1.3rem + 0.6vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h3 {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h4 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
abbr[title],
|
||||||
|
abbr[data-bs-original-title] {
|
||||||
|
-webkit-text-decoration: underline dotted;
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
cursor: help;
|
||||||
|
-webkit-text-decoration-skip-ink: none;
|
||||||
|
text-decoration-skip-ink: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
address {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-style: normal;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul {
|
||||||
|
padding-right: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul,
|
||||||
|
dl {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol ol,
|
||||||
|
ul ul,
|
||||||
|
ol ul,
|
||||||
|
ul ol {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark {
|
||||||
|
padding: 0.2em;
|
||||||
|
background-color: #fcf8e3;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
position: relative;
|
||||||
|
font-size: 0.75em;
|
||||||
|
line-height: 0;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #0d6efd;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: #0a58ca;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre,
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
|
font-size: 1em;
|
||||||
|
direction: ltr ;
|
||||||
|
unicode-bidi: bidi-override;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
display: block;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
overflow: auto;
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
pre code {
|
||||||
|
font-size: inherit;
|
||||||
|
color: inherit;
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-size: 0.875em;
|
||||||
|
color: #d63384;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
a > code {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
kbd {
|
||||||
|
padding: 0.2rem 0.4rem;
|
||||||
|
font-size: 0.875em;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #212529;
|
||||||
|
border-radius: 0.2rem;
|
||||||
|
}
|
||||||
|
kbd kbd {
|
||||||
|
padding: 0;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
svg {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
caption-side: bottom;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
caption {
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
color: #6c757d;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: inherit;
|
||||||
|
text-align: -webkit-match-parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead,
|
||||||
|
tbody,
|
||||||
|
tfoot,
|
||||||
|
tr,
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border-color: inherit;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus:not(:focus-visible) {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
button,
|
||||||
|
select,
|
||||||
|
optgroup,
|
||||||
|
textarea {
|
||||||
|
margin: 0;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[role=button] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
word-wrap: normal;
|
||||||
|
}
|
||||||
|
select:disabled {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[list]::-webkit-calendar-picker-indicator {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type=button],
|
||||||
|
[type=reset],
|
||||||
|
[type=submit] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
button:not(:disabled),
|
||||||
|
[type=button]:not(:disabled),
|
||||||
|
[type=reset]:not(:disabled),
|
||||||
|
[type=submit]:not(:disabled) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-moz-focus-inner {
|
||||||
|
padding: 0;
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
min-width: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
float: right;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
legend {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
legend + * {
|
||||||
|
clear: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-datetime-edit-fields-wrapper,
|
||||||
|
::-webkit-datetime-edit-text,
|
||||||
|
::-webkit-datetime-edit-minute,
|
||||||
|
::-webkit-datetime-edit-hour-field,
|
||||||
|
::-webkit-datetime-edit-day-field,
|
||||||
|
::-webkit-datetime-edit-month-field,
|
||||||
|
::-webkit-datetime-edit-year-field {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-inner-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type=search] {
|
||||||
|
outline-offset: -2px;
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="tel"],
|
||||||
|
[type="url"],
|
||||||
|
[type="email"],
|
||||||
|
[type="number"] {
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-color-swatch-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::file-selector-button {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
font: inherit;
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,8 @@
|
|||||||
|
/*!
|
||||||
|
* Bootstrap Reboot v5.1.0 (https://getbootstrap.com/)
|
||||||
|
* Copyright 2011-2021 The Bootstrap Authors
|
||||||
|
* Copyright 2011-2021 Twitter, Inc.
|
||||||
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||||
|
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||||
|
*/*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-right:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-right:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:right}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:right;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:right}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}[type=email],[type=number],[type=tel],[type=url]{direction:ltr}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}
|
||||||
|
/*# sourceMappingURL=bootstrap-reboot.rtl.min.css.map */
|
File diff suppressed because one or more lines are too long
4866
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css
vendored
Normal file
4866
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4857
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css
vendored
Normal file
4857
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
11221
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/css/bootstrap.css
vendored
Normal file
11221
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/css/bootstrap.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
11197
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css
vendored
Normal file
11197
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
6780
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js
vendored
Normal file
6780
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4977
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js
vendored
Normal file
4977
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
5026
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/js/bootstrap.js
vendored
Normal file
5026
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/js/bootstrap.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map
vendored
Normal file
1
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
7
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js
vendored
Normal file
7
FurnitureAssembly/FurnitureAssemblyClientApp/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user