Персист модалок: черновики, восстановление после F5, предупреждение при выходе #30

Merged
gorbenko.pavel merged 7 commits from feature/form-drafts into main 2026-10-19 04:46:16 +00:00
3 changed files with 18 additions and 6 deletions
Showing only changes of commit 2eccd304bb - Show all commits
@@ -20,6 +20,18 @@ public static class StepLabels
_ => headType.ToString()
};
/// <summary>Название с маленькой буквы — для середины фразы («Не найден руководитель направления»).</summary>
public static string InSentence(HeadType headType) => Lower(Of(headType));
/// <summary>
/// Все названия мужского рода («найден руководитель проекта»), кроме «Безопасность» —
/// для неё нужна отдельная формулировка, иначе выйдет «найден безопасность».
/// </summary>
public static string InSentence(ProjectRole projectRole) =>
projectRole == ProjectRole.Security ? "сотрудник службы безопасности" : Lower(Of(projectRole));
private static string Lower(string label) => char.ToLowerInvariant(label[0]) + label[1..];
public static string Of(ProjectRole projectRole) => projectRole switch
{
ProjectRole.ProjectManager => "Руководитель проекта",
@@ -438,7 +438,7 @@ public sealed class TemplateRouteService(
{
resolvedEmail = projectExecutors.FirstOrDefault(e => e.ProjectRole == step.ProjectRole)?.Email;
if (string.IsNullOrWhiteSpace(resolvedEmail))
warning = $"Не найден исполнитель с ролью в проекте «{StepLabels.Of(step.ProjectRole.Value)}».";
warning = $"В проекте не найден {StepLabels.InSentence(step.ProjectRole.Value)}.";
}
// DepartmentHead — единственный HeadType, который резолвится НЕ по цепочке
// подчинённости инициатора, а по отделу, переданному явно в запросе (см.
@@ -471,12 +471,12 @@ public sealed class TemplateRouteService(
};
if (string.IsNullOrWhiteSpace(resolvedEmail))
{
var headLabel = StepLabels.Of(step.HeadType.Value);
var headLabel = StepLabels.InSentence(step.HeadType.Value);
// Директор Tansu и управляющий директор берутся из настроек сервиса, а не из
// справочника сотрудников — там email не «не найден», а «не проставлен».
warning = step.HeadType is HeadType.DirectorTansu or HeadType.ManagingDirectorTansu
? $"Не проставлен «{headLabel}»: email не задан в настройках сервиса."
: $"Не найден руководитель «{headLabel}»: не указан у инициатора в справочнике сотрудников.";
? $"Не проставлен {headLabel}."
: $"Не найден {headLabel}.";
}
}
else
@@ -1051,7 +1051,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
resolved.Value.Steps[0].ExecutorEmail.ShouldBeNull();
var warning = resolved.Value.Steps[0].Warning;
warning.ShouldNotBeNull();
warning.ShouldContain("руководитель");
warning.ShouldBe("Не найден непосредственный руководитель.");
}
[Fact(DisplayName = "Резолв шага с должностью возвращает warning, если сотрудник по должности не найден")]
@@ -1109,7 +1109,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
resolved.Value.Steps[0].ExecutorEmail.ShouldBeNull();
var warning = resolved.Value.Steps[0].Warning;
warning.ShouldNotBeNull();
warning.ShouldContain("роль");
warning.ShouldBe("В проекте не найден сотрудник службы безопасности.");
}
[Fact(DisplayName = "Резолв маршрута откатывается на маршрут без отдела, если точного совпадения по отделу нет")]