Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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"" ")

Check notice

Code scanning / CodeQL

Redundant ToString() call

Redundant call to 'ToString' on a String object.
.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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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<ElectionRoundsMonitoringResult>(
$"/api/election-rounds:monitoring");

var betaNgoElectionRounds = scenarioData.NgoByName(ScenarioNgos.Beta).Admin
.GetResponse<ElectionRoundsMonitoringResult>(
$"/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();
}
}
1 change: 0 additions & 1 deletion utils/SubmissionsFaker/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ await AnsiConsole.Progress()
.WithMonitoringNgo(ScenarioNgos.Beta))
.Please();


await scenarioData.PlatformAdminClient.CreatePSIForm(scenarioData.ElectionRoundId, PSIFormData.PSIForm);
setupTask.Increment(1);

Expand Down
8 changes: 6 additions & 2 deletions web/src/components/layout/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,12 @@ const Header = (): FunctionComponent => {
<DropdownMenu>
<DropdownMenuTrigger>
<Badge className='w-[360px] bg-secondary-300 text-secondary-900 hover:bg-secondary-300/90'>
<div className='election-text'>{selectedElectionRound?.title}</div>
<ChevronDownIcon className='w-[20px] ml-2' />
<div className='flex items-center justify-between gap-2 w-full'>
<div className='truncate max-w-[300px]' title={selectedElectionRound?.title}>
{selectedElectionRound?.title}
</div>
<ChevronDownIcon className='w-[20px] ml-2' />
</div>
</Badge>
</DropdownMenuTrigger>
<DropdownMenuContent>
Expand Down
36 changes: 26 additions & 10 deletions web/src/features/forms/components/Dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,9 @@ export default function FormsDashboard(): ReactElement {
View
</DropdownMenuItem>
{row.depth === 0 && row.original.status === FormStatus.Published ? (
<DropdownMenuItem onClick={() => editFormAccessDialog.trigger(row.original.id)}>Form access</DropdownMenuItem>
<DropdownMenuItem onClick={() => editFormAccessDialog.trigger(row.original.id)}>
Form access
</DropdownMenuItem>
) : null}
{row.depth === 0 ? (
<DropdownMenuItem
Expand All @@ -314,7 +316,7 @@ export default function FormsDashboard(): ReactElement {
Add translations
</DropdownMenuItem>
) : null}

{row.depth === 0 && row.original.status === FormStatus.Published ? (
<DropdownMenuItem onClick={() => handleObsoleteForm(row.original)}>Obsolete</DropdownMenuItem>
) : null}
Expand All @@ -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, <b>the associated data cannot be retrieved</b>
form could result in the loss of any submitted answers from your observers. Once deleted,
<b>the associated data cannot be retrieved</b>
</>
) : (
'This action is permanent and cannot be undone. Once deleted, this form cannot be retrieved.'
Expand Down Expand Up @@ -404,37 +406,50 @@ export default function FormsDashboard(): ReactElement {

{row.depth === 0 ? (
<DropdownMenuItem
disabled={row.original.status !== FormStatus.Drafted}
disabled={!row.original.isFormOwner || row.original.status !== FormStatus.Drafted}
onClick={() => navigateToEdit(row.original.id)}>
Edit
</DropdownMenuItem>
) : (
<DropdownMenuItem
disabled={row.original.status !== FormStatus.Drafted}
disabled={!row.original.isFormOwner || row.original.status !== FormStatus.Drafted}
onClick={() => navigateToEditTranslation(row.original.id, row.original.defaultLanguage)}>
Edit
</DropdownMenuItem>
)}

{row.depth === 0 ? (
<DropdownMenuItem
disabled={row.original.status !== FormStatus.Drafted}
disabled={!row.original.isFormOwner || row.original.status !== FormStatus.Drafted}
onClick={() => addTranslationsDialog.trigger(row.original.id, row.original.languages)}>
Add translations
</DropdownMenuItem>
) : null}
{row.depth === 0 && row.original.status === FormStatus.Published ? (
<DropdownMenuItem onClick={() => handleObsoleteForm(row.original)}>Obsolete</DropdownMenuItem>
<DropdownMenuItem
disabled={!row.original.isFormOwner}
onClick={() => handleObsoleteForm(row.original)}>
Obsolete
</DropdownMenuItem>
) : null}
{row.depth === 0 && row.original.status === FormStatus.Drafted ? (
<DropdownMenuItem onClick={() => handlePublishForm(row.original)}>Publish</DropdownMenuItem>
<DropdownMenuItem
disabled={!row.original.isFormOwner}
onClick={() => handlePublishForm(row.original)}>
Publish
</DropdownMenuItem>
) : null}
{row.depth === 0 ? (
<DropdownMenuItem onClick={() => handleDuplicateForm(row.original)}>Duplicate</DropdownMenuItem>
<DropdownMenuItem
disabled={!row.original.isFormOwner}
onClick={() => handleDuplicateForm(row.original)}>
Duplicate
</DropdownMenuItem>
) : null}
{row.depth === 0 ? (
<DropdownMenuItem
className='text-red-600'
disabled={!row.original.isFormOwner}
onClick={async () => {
if (
await confirm({
Expand Down Expand Up @@ -465,6 +480,7 @@ export default function FormsDashboard(): ReactElement {
) : (
<DropdownMenuItem
className='text-red-600'
disabled={!row.original.isFormOwner}
onClick={async () => {
const languageCode = row.original.defaultLanguage;
const language = languages?.find((l) => languageCode === l.code);
Expand Down
1 change: 1 addition & 0 deletions web/src/features/forms/models/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface FormBase {
icon?: string;
name: TranslatedString;
description?: TranslatedString;
isFormOwner: boolean;
status: FormStatus;
languages: string[];
lastModifiedOn: string;
Expand Down