feat(backend): add communication, tester, admin and middleman modules

Implement step 17 modules: threads/messages, notifications with automation (submission.created, idea.created, grant_call.opened), innovation test feedback, admin panel (submissions, verify, metrics, grant call status), Middleman adaptation and partnerships. Split multi-type files into one type per file and replace object-valued dictionaries with string-valued maps in data contracts (migrations 0006/0007).
This commit is contained in:
Rafał Tyński
2026-10-04 03:49:45 +02:00
parent f4f3b3896a
commit fa1199539b
136 changed files with 2338 additions and 574 deletions
@@ -68,7 +68,7 @@ public sealed class InnovationService : IInnovationService
["challengeTable"] = challengeTable,
["challengeId"] = challengeId,
["stage"] = request.Stage,
["media"] = request.Media ?? [],
["media"] = ToConcreteList(request.Media),
["published"] = request.Published,
};
@@ -112,7 +112,7 @@ public sealed class InnovationService : IInnovationService
["challengeTable"] = challengeTable,
["challengeId"] = challengeId,
["stage"] = request.Stage,
["media"] = request.Media ?? [],
["media"] = ToConcreteList(request.Media),
["published"] = request.Published,
};
@@ -200,13 +200,13 @@ public sealed class InnovationService : IInnovationService
List<string> assignments,
Dictionary<string, object?> parameters,
string field,
IReadOnlyDictionary<string, object?>? value,
IReadOnlyDictionary<string, string?>? value,
bool clearWhenMissing = false)
{
if (value is not null)
{
assignments.Add($"{field} = $field_{field}");
parameters[$"field_{field}"] = value;
parameters[$"field_{field}"] = value.ToDictionary(pair => pair.Key, pair => pair.Value);
}
else if (clearWhenMissing)
{
@@ -214,6 +214,10 @@ public sealed class InnovationService : IInnovationService
}
}
private static List<Dictionary<string, string?>> ToConcreteList(
IReadOnlyList<IReadOnlyDictionary<string, string?>>? values) =>
values?.Select(item => item.ToDictionary(pair => pair.Key, pair => pair.Value)).ToList() ?? [];
private static InnovationResponse Map(InnovationRow row) => new(
row.Id,
row.Title,
@@ -222,8 +226,8 @@ public sealed class InnovationService : IInnovationService
row.Challenge,
row.Stage,
row.Cost,
row.Contact is null ? null : row.Contact,
row.Media.Select(item => (IReadOnlyDictionary<string, object?>)item).ToList(),
row.Contact,
row.Media.Select(item => (IReadOnlyDictionary<string, string?>)item).ToList(),
row.Published,
row.UpdatedAt);
}