diff --git a/api/src/Vote.Monitor.Hangfire/Jobs/Export/FormSubmissions/ExportFormSubmissionsJob.cs b/api/src/Vote.Monitor.Hangfire/Jobs/Export/FormSubmissions/ExportFormSubmissionsJob.cs index 6be1bab57..5ce96d077 100644 --- a/api/src/Vote.Monitor.Hangfire/Jobs/Export/FormSubmissions/ExportFormSubmissionsJob.cs +++ b/api/src/Vote.Monitor.Hangfire/Jobs/Export/FormSubmissions/ExportFormSubmissionsJob.cs @@ -51,17 +51,18 @@ public async Task Run(Guid electionRoundId, Guid ngoId, Guid exportedDataId, Can .Where(x => x.ElectionRoundId == electionRoundId) .AsNoTracking() .FirstOrDefaultAsync(ct); + var filters = exportedData.FormSubmissionsFilters ?? new ExportFormSubmissionsFilters(); var publishedForms = await context .Forms - .Where(x => x.ElectionRoundId == electionRoundId - && x.MonitoringNgo.NgoId == ngoId - && x.Status == FormStatus.Published) + .FromSqlInterpolated( + @$"SELECT f.* FROM ""Forms"" f + INNER JOIN ""GetAvailableForms""({electionRoundId}, {ngoId}, {filters.DataSource.ToString()}) af on af.""FormId"" = f.""Id"" ") + .Where(x=>x.Status == FormStatus.Published) .OrderBy(x => x.CreatedOn) .AsNoTracking() .ToListAsync(ct); - var filters = exportedData.FormSubmissionsFilters ?? new ExportFormSubmissionsFilters(); var submissions = await GetSubmissions(electionRoundId, ngoId, filters, ct); foreach (var submission in submissions) diff --git a/api/tests/Vote.Monitor.Api.IntegrationTests/Features/ElectionRounds/GetMonitoringTests.cs b/api/tests/Vote.Monitor.Api.IntegrationTests/Features/ElectionRounds/GetMonitoringTests.cs new file mode 100644 index 000000000..e328f2298 --- /dev/null +++ b/api/tests/Vote.Monitor.Api.IntegrationTests/Features/ElectionRounds/GetMonitoringTests.cs @@ -0,0 +1,86 @@ +using Vote.Monitor.Api.IntegrationTests.Consts; +using Vote.Monitor.Api.IntegrationTests.Scenarios; +using ElectionRoundsMonitoringResult = Vote.Monitor.Api.Feature.ElectionRound.Monitoring.Result; + +namespace Vote.Monitor.Api.IntegrationTests.Features.ElectionRounds; + +using static ApiTesting; + +public class GetMonitoringTests : BaseApiTestFixture +{ + [Test] + public void ShouldReturnCorrectElectionRoundDetails() + { + // Arrange + var scenarioData = ScenarioBuilder.New(CreateClient) + .WithNgo(ScenarioNgos.Alfa) + .WithNgo(ScenarioNgos.Beta) + .WithElectionRound(ScenarioElectionRound.A, er => er + .WithCoalition(ScenarioCoalition.Youth, ScenarioNgos.Alfa, [ScenarioNgos.Beta]) + ) + .WithElectionRound(ScenarioElectionRound.B, er => er + .WithMonitoringNgo(ScenarioNgo.Beta) + ) + .WithElectionRound(ScenarioElectionRound.C, er => er + .WithMonitoringNgo(ScenarioNgo.Alfa) + .WithMonitoringNgo(ScenarioNgo.Beta) + ) + .Please(); + + var electionRoundAId = scenarioData.ElectionRoundIdByName(ScenarioElectionRound.A); + var electionRoundBId = scenarioData.ElectionRoundIdByName(ScenarioElectionRound.B); + var electionRoundCId = scenarioData.ElectionRoundIdByName(ScenarioElectionRound.C); + // Act + var alfaNgoElectionRounds = scenarioData.NgoByName(ScenarioNgos.Alfa).Admin + .GetResponse( + $"/api/election-rounds:monitoring"); + + var betaNgoElectionRounds = scenarioData.NgoByName(ScenarioNgos.Beta).Admin + .GetResponse( + $"/api/election-rounds:monitoring"); + + // Assert + alfaNgoElectionRounds.ElectionRounds + .Should() + .HaveCount(2); + + alfaNgoElectionRounds + .ElectionRounds + .First(x => x.ElectionRoundId == electionRoundAId) + .IsCoalitionLeader + .Should() + .BeTrue(); + + alfaNgoElectionRounds + .ElectionRounds + .First(x => x.ElectionRoundId == electionRoundCId) + .IsCoalitionLeader + .Should() + .BeFalse(); + + betaNgoElectionRounds.ElectionRounds + .Should() + .HaveCount(3); + + betaNgoElectionRounds + .ElectionRounds + .First(x => x.ElectionRoundId == electionRoundAId) + .IsCoalitionLeader + .Should() + .BeFalse(); + + betaNgoElectionRounds + .ElectionRounds + .First(x => x.ElectionRoundId == electionRoundBId) + .IsCoalitionLeader + .Should() + .BeFalse(); + + betaNgoElectionRounds + .ElectionRounds + .First(x => x.ElectionRoundId == electionRoundCId) + .IsCoalitionLeader + .Should() + .BeFalse(); + } +} diff --git a/utils/SubmissionsFaker/Program.cs b/utils/SubmissionsFaker/Program.cs index 807dd167d..88516995a 100644 --- a/utils/SubmissionsFaker/Program.cs +++ b/utils/SubmissionsFaker/Program.cs @@ -134,7 +134,6 @@ await AnsiConsole.Progress() .WithMonitoringNgo(ScenarioNgos.Beta)) .Please(); - await scenarioData.PlatformAdminClient.CreatePSIForm(scenarioData.ElectionRoundId, PSIFormData.PSIForm); setupTask.Increment(1); diff --git a/web/src/components/layout/Header/Header.tsx b/web/src/components/layout/Header/Header.tsx index 62b88d74d..b2e853818 100644 --- a/web/src/components/layout/Header/Header.tsx +++ b/web/src/components/layout/Header/Header.tsx @@ -153,8 +153,12 @@ const Header = (): FunctionComponent => { -
{selectedElectionRound?.title}
- +
+
+ {selectedElectionRound?.title} +
+ +
diff --git a/web/src/features/forms/components/Dashboard/Dashboard.tsx b/web/src/features/forms/components/Dashboard/Dashboard.tsx index d0b0f1bd6..43b3f8b30 100644 --- a/web/src/features/forms/components/Dashboard/Dashboard.tsx +++ b/web/src/features/forms/components/Dashboard/Dashboard.tsx @@ -291,7 +291,9 @@ export default function FormsDashboard(): ReactElement { View {row.depth === 0 && row.original.status === FormStatus.Published ? ( - editFormAccessDialog.trigger(row.original.id)}>Form access + editFormAccessDialog.trigger(row.original.id)}> + Form access + ) : null} {row.depth === 0 ? ( ) : null} - + {row.depth === 0 && row.original.status === FormStatus.Published ? ( handleObsoleteForm(row.original)}>Obsolete ) : null} @@ -335,8 +337,8 @@ export default function FormsDashboard(): ReactElement { row.original.status === FormStatus.Published ? ( <> Please note that this form is published and may contain associated data. Deleting this - form could result in the loss of any submitted answers from your observers. Once - deleted, the associated data cannot be retrieved + form could result in the loss of any submitted answers from your observers. Once deleted, + the associated data cannot be retrieved ) : ( 'This action is permanent and cannot be undone. Once deleted, this form cannot be retrieved.' @@ -404,13 +406,13 @@ export default function FormsDashboard(): ReactElement { {row.depth === 0 ? ( navigateToEdit(row.original.id)}> Edit ) : ( navigateToEditTranslation(row.original.id, row.original.defaultLanguage)}> Edit @@ -418,23 +420,36 @@ export default function FormsDashboard(): ReactElement { {row.depth === 0 ? ( addTranslationsDialog.trigger(row.original.id, row.original.languages)}> Add translations ) : null} {row.depth === 0 && row.original.status === FormStatus.Published ? ( - handleObsoleteForm(row.original)}>Obsolete + handleObsoleteForm(row.original)}> + Obsolete + ) : null} {row.depth === 0 && row.original.status === FormStatus.Drafted ? ( - handlePublishForm(row.original)}>Publish + handlePublishForm(row.original)}> + Publish + ) : null} {row.depth === 0 ? ( - handleDuplicateForm(row.original)}>Duplicate + handleDuplicateForm(row.original)}> + Duplicate + ) : null} {row.depth === 0 ? ( { if ( await confirm({ @@ -465,6 +480,7 @@ export default function FormsDashboard(): ReactElement { ) : ( { const languageCode = row.original.defaultLanguage; const language = languages?.find((l) => languageCode === l.code); diff --git a/web/src/features/forms/models/form.ts b/web/src/features/forms/models/form.ts index 8efff3083..7a40471fd 100644 --- a/web/src/features/forms/models/form.ts +++ b/web/src/features/forms/models/form.ts @@ -25,6 +25,7 @@ export interface FormBase { icon?: string; name: TranslatedString; description?: TranslatedString; + isFormOwner: boolean; status: FormStatus; languages: string[]; lastModifiedOn: string;