Skip to content

Commit 923c9c9

Browse files
imdeaconuidormenco
andauthored
[PlatformAdmin] add views and CRUD logic for NGO management (#860)
* fix: make dropdown menus scrollable * fix: truncate overflowing table columns * Squashed commit of the following: commit 742f250 Author: imdeaconu <imdeaconu@gmail.com> Date: Wed Sep 11 19:54:55 2024 +0300 add read notification checkmark commit ea11fa0 Author: imdeaconu <imdeaconu@gmail.com> Date: Wed Sep 11 19:54:30 2024 +0300 add read notification column * Squashed commit of the following: commit d8833dc Author: imdeaconu <imdeaconu@gmail.com> Date: Fri Sep 13 13:29:31 2024 +0300 WIP: add selector functionality commit 3608c0e Author: imdeaconu <imdeaconu@gmail.com> Date: Fri Sep 13 10:00:05 2024 +0300 WIP: create new tags input * chore: remove unused import * chore: delete duplicated / unused classes * feature: add searching to MonitoringObserversTagFilter * chore: update config files * Revert "[NGO Admin] Rewrite the tag selector component (#675)" This reverts commit 2ad0e90. * Merge branch 'main' of https://github.com/commitglobal/votemonitor into commitglobal-main * WIP: add search and filtering to NGO list * WIP: add views for NGO Details * WIP: add NGO activation / deactivation * WIP: implement NGO delete * WIP: add NGO create modal * WIP: add modal for adding admins to NGOs * WIP: add CRUD for NGO admins * WIP: add ngo admin details view * WIP: fix back buttons * WIP: fix breadcrumbs * WIP: fix undefined ngo name in breadcrumbs * WIP: add form for editing NGO admins * WIPȘ fix NGO name disappearing from breadcrumbs * WIP: add new NGO data to table and details view * WIP: add password field for admin creation * WIP: move admin queries to a separate hook * WIP: refactor NGOs query logic * WIP: refactor debounced search & query params handling * WIP: add NGO editing * WIP: create Base zod search schemas for search params * WIP: remove console.log * WIP: remove incomplete subtitle * fix missing imports * WIP: remove ID column and rename ngo queries file * WIP: remove zod schema extension * WIP: change auth check for NGO routes to PlatformAdmin * WIP: remove div wrapper * WIP: remove URL generator function * WIP: remove NGO admin from NGO creation * WIP: remove translation strings * WIP: remove unused var * fix build * bugfixes, improvements and cleanup --------- Co-authored-by: Ion Dormenco <idormenco@gmail.com>
1 parent 5a2c6db commit 923c9c9

File tree

45 files changed

+2115
-195
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2115
-195
lines changed

api/src/Vote.Monitor.Api.Feature.Ngo/Create/Endpoint.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
namespace Vote.Monitor.Api.Feature.Ngo.Create;
55

66
public class Endpoint(IRepository<NgoAggregate> repository) :
7-
Endpoint<Request, Results<Ok<NgoModel>, Conflict<ProblemDetails>>>
7+
Endpoint<Request, Results<Ok<NgoModel>, ProblemDetails>>
88
{
99
public override void Configure()
1010
{
@@ -14,7 +14,7 @@ public override void Configure()
1414
Policies(PolicyNames.PlatformAdminsOnly);
1515
}
1616

17-
public override async Task<Results<Ok<NgoModel>, Conflict<ProblemDetails>>> ExecuteAsync(Request req,
17+
public override async Task<Results<Ok<NgoModel>, ProblemDetails>> ExecuteAsync(Request req,
1818
CancellationToken ct)
1919
{
2020
var specification = new GetNgoByNameSpecification(req.Name);
@@ -23,7 +23,7 @@ public override async Task<Results<Ok<NgoModel>, Conflict<ProblemDetails>>> Exec
2323
if (hasNgoWithSameName)
2424
{
2525
AddError(r => r.Name, "A Ngo with same name already exists");
26-
return TypedResults.Conflict(new ProblemDetails(ValidationFailures));
26+
return new ProblemDetails(ValidationFailures);
2727
}
2828

2929
var ngo = new NgoAggregate(req.Name);

api/src/Vote.Monitor.Api.Feature.NgoAdmin/Create/Endpoint.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace Vote.Monitor.Api.Feature.NgoAdmin.Create;
77
public class Endpoint(
88
UserManager<ApplicationUser> userManager,
99
IRepository<NgoAdminAggregate> repository)
10-
: Endpoint<Request, Results<Ok<NgoAdminModel>, Conflict<ProblemDetails>>>
10+
: Endpoint<Request, Results<Ok<NgoAdminModel>, ProblemDetails>>
1111
{
1212
public override void Configure()
1313
{
@@ -18,14 +18,14 @@ public override void Configure()
1818
Policies(PolicyNames.PlatformAdminsOnly);
1919
}
2020

21-
public override async Task<Results<Ok<NgoAdminModel>, Conflict<ProblemDetails>>> ExecuteAsync(Request req,
21+
public override async Task<Results<Ok<NgoAdminModel>, ProblemDetails>> ExecuteAsync(Request req,
2222
CancellationToken ct)
2323
{
2424
var user = await userManager.FindByEmailAsync(req.Email);
2525
if (user is not null)
2626
{
27-
AddError(r => r.Email, "A ngo admin with same login already exists");
28-
return TypedResults.Conflict(new ProblemDetails(ValidationFailures));
27+
AddError(r => r.Email, "A user with same login already exists");
28+
return new ProblemDetails(ValidationFailures);
2929
}
3030

3131
var applicationUser =
@@ -35,7 +35,7 @@ public override async Task<Results<Ok<NgoAdminModel>, Conflict<ProblemDetails>>>
3535
if (!result.Succeeded)
3636
{
3737
AddError(r => r.Email, result.GetAllErrors());
38-
return TypedResults.Conflict(new ProblemDetails(ValidationFailures));
38+
return new ProblemDetails(ValidationFailures);
3939
}
4040

4141
var ngoAdmin = new NgoAdminAggregate(req.NgoId, applicationUser);

api/src/Vote.Monitor.Api.Feature.NgoAdmin/Update/Endpoint.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ public override async Task<Results<NoContent, NotFound, ValidationProblem>> Exec
2929
}
3030

3131
ngoAdmin.ApplicationUser.UpdateDetails(req.FirstName, req.LastName, req.PhoneNumber);
32-
ngoAdmin.ApplicationUser.UpdateStatus(req.Status);
3332
var result = await userManager.UpdateAsync(ngoAdmin.ApplicationUser);
3433

3534
if (!result.Succeeded)

api/src/Vote.Monitor.Api.Feature.NgoAdmin/Update/Request.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,4 @@ public class Request
77
public string FirstName { get; set; }
88
public string LastName { get; set; }
99
public string? PhoneNumber { get; set; }
10-
11-
public UserStatus Status { get; set; }
1210
}

api/tests/Vote.Monitor.Api.Feature.Ngo.UnitTests/Endpoints/CreateEndpointTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ await repository
2929
.AddAsync(Arg.Is<NgoAggregate>(x => x.Name == ngoName));
3030

3131
result
32-
.Should().BeOfType<Results<Ok<NgoModel>, Conflict<ProblemDetails>>>()!
32+
.Should().BeOfType<Results<Ok<NgoModel>, ProblemDetails>>()!
3333
.Which!
3434
.Result.Should().BeOfType<Ok<NgoModel>>()!
3535
.Which!.Value!.Name.Should().Be(ngoName);
@@ -53,8 +53,8 @@ public async Task ShouldReturnConflict_WhenNgoWithSameNameExists()
5353

5454
// Assert
5555
result
56-
.Should().BeOfType<Results<Ok<NgoModel>, Conflict<ProblemDetails>>>()
56+
.Should().BeOfType<Results<Ok<NgoModel>, ProblemDetails>>()
5757
.Which
58-
.Result.Should().BeOfType<Conflict<ProblemDetails>>();
58+
.Result.Should().BeOfType<ProblemDetails>();
5959
}
6060
}

web/src/common/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,3 +409,12 @@ export interface FormBase {
409409
numberOfQuestions: number;
410410
languagesTranslationStatus: LanguagesTranslationStatus;
411411
}
412+
413+
export interface ProblemDetails {
414+
type: string;
415+
title: string;
416+
status: number;
417+
detail: string;
418+
instance?: string;
419+
errors?: { name: string; reason: string }[]; // Maps field names to error messages
420+
}

web/src/common/zod-schemas.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { z } from 'zod';
2+
import { SortOrder } from './types';
3+
4+
export const PageParametersBaseSchema = z.object({
5+
pageNumber: z.number().catch(1),
6+
pageSize: z.number().catch(10),
7+
});
8+
9+
export const SortParamsBaseSchema = z.object({
10+
sortColumnName: z.string().catch(''),
11+
searchText: z.coerce.string().optional(),
12+
sortOrder: z.nativeEnum(SortOrder).catch(SortOrder.asc),
13+
});
14+
15+
export const DefaultSearchParamsSchema = PageParametersBaseSchema.merge(SortParamsBaseSchema);

web/src/components/layout/Header/Header.tsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,10 @@ const Header = (): FunctionComponent => {
4747
const navigate = useNavigate();
4848
const [selectedElectionRound, setSelectedElection] = useState<ElectionEvent>();
4949
const router = useRouter();
50-
const {
51-
setCurrentElectionRoundId,
52-
currentElectionRoundId,
53-
} = useCurrentElectionRoundStore((s) => s);
50+
const { setCurrentElectionRoundId, currentElectionRoundId } = useCurrentElectionRoundStore((s) => s);
5451

5552
const handleSelectElectionRound = async (electionRound?: ElectionEvent): Promise<void> => {
56-
if (electionRound && selectedElectionRound?.id != electionRound.id ) {
53+
if (electionRound && selectedElectionRound?.id != electionRound.id) {
5754
setSelectedElection(electionRound);
5855
setCurrentElectionRoundId(electionRound.id);
5956

@@ -149,7 +146,9 @@ const Header = (): FunctionComponent => {
149146
</div>
150147

151148
<div className='items-center hidden gap-2 md:flex'>
152-
{userRole !== 'NgoAdmin'? <></> : status === 'pending' ? (
149+
{userRole !== 'NgoAdmin' ? (
150+
<></>
151+
) : status === 'pending' ? (
153152
<Skeleton className='w-[360px] h-[26px] mr-2 rounded-lg bg-secondary-300 text-secondary-900 hover:bg-secondary-300/90' />
154153
) : (
155154
<DropdownMenu>
@@ -173,9 +172,7 @@ const Header = (): FunctionComponent => {
173172
<DropdownMenuLabel> Upcomming elections </DropdownMenuLabel>
174173

175174
{activeElections?.map((electionRound) => (
176-
<DropdownMenuRadioItem
177-
key={electionRound.id}
178-
value={electionRound.id}>
175+
<DropdownMenuRadioItem key={electionRound.id} value={electionRound.id}>
179176
<div className='flex items-center gap-2'>
180177
{electionRound?.status === ElectionRoundStatus.NotStarted ? (
181178
<PauseCircleIcon className='w-4 h-4 text-slate-700' />
@@ -192,9 +189,7 @@ const Header = (): FunctionComponent => {
192189
<DropdownMenuSeparator />
193190
<DropdownMenuLabel> Archived elections </DropdownMenuLabel>
194191
{archivedElections?.map((electionRound) => (
195-
<DropdownMenuRadioItem
196-
key={electionRound.id}
197-
value={electionRound.id}>
192+
<DropdownMenuRadioItem key={electionRound.id} value={electionRound.id}>
198193
<div className='flex items-center gap-2'>
199194
<StopCircleIcon className='w-4 h-4 text-yellow-700' />
200195

web/src/features/CitizenNotifications/CitizenNotificationMessageForm/CitizenNotificationMessageForm.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ function CitizenNotificationMessageForm(): FunctionComponent {
3636

3737
const form = useForm<z.infer<typeof createPushMessageSchema>>({
3838
resolver: zodResolver(createPushMessageSchema),
39+
mode: 'all',
3940
defaultValues: {
4041
title: '',
4142
messageBody: '',

web/src/features/auth/AcceptInvite.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ function AcceptInvite() {
3939

4040
const form = useForm<z.infer<typeof formSchema>>({
4141
resolver: zodResolver(formSchema),
42+
mode: 'all',
4243
defaultValues: {
4344
password: '',
4445
confirmPassword: '',

0 commit comments

Comments
 (0)