From efa3813f53246b2b1d38828e3a594030b3d80a8d Mon Sep 17 00:00:00 2001 From: Iulian Deaconu Date: Tue, 22 Oct 2024 21:36:50 +0300 Subject: [PATCH] [NGO Admin] Move the form for adding individual observers into a modal (#791) * fix: make dropdown menus scrollable * fix: truncate overflowing table columns * Squashed commit of the following: commit 742f25001369978fc4b9d03a851c2f1ef72024a7 Author: imdeaconu Date: Wed Sep 11 19:54:55 2024 +0300 add read notification checkmark commit ea11fa0f637ad2e00cc7a8601c6c6f51fcb64a3f Author: imdeaconu Date: Wed Sep 11 19:54:30 2024 +0300 add read notification column * Squashed commit of the following: commit d8833dcf5669c257a28ed0bd58f5085385f2b53f Author: imdeaconu Date: Fri Sep 13 13:29:31 2024 +0300 WIP: add selector functionality commit 3608c0e7d3d79a26037f8b7961d50f019b924406 Author: imdeaconu 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 2ad0e909be5117b4d5deb369015c428321c23dea. * Merge branch 'main' of https://github.com/commitglobal/votemonitor into commitglobal-main * move the create observer form into a modal * remove create observer route --- .../CreateMonitoringObserverDialog.tsx} | 79 ++++---- .../MonitoringObserversList.tsx | 177 +++++++++--------- web/src/locales/en.json | 20 +- web/src/routeTree.gen.ts | 12 -- .../monitoring-observers/new-observer.tsx | 14 -- 5 files changed, 128 insertions(+), 174 deletions(-) rename web/src/features/monitoring-observers/components/{CreateMonitoringObserver.tsx => MonitoringObserversList/CreateMonitoringObserverDialog.tsx} (73%) delete mode 100644 web/src/routes/monitoring-observers/new-observer.tsx diff --git a/web/src/features/monitoring-observers/components/CreateMonitoringObserver.tsx b/web/src/features/monitoring-observers/components/MonitoringObserversList/CreateMonitoringObserverDialog.tsx similarity index 73% rename from web/src/features/monitoring-observers/components/CreateMonitoringObserver.tsx rename to web/src/features/monitoring-observers/components/MonitoringObserversList/CreateMonitoringObserverDialog.tsx index 8bd9e07a5..79e61078d 100644 --- a/web/src/features/monitoring-observers/components/CreateMonitoringObserver.tsx +++ b/web/src/features/monitoring-observers/components/MonitoringObserversList/CreateMonitoringObserverDialog.tsx @@ -1,28 +1,28 @@ import { authApi } from '@/common/auth-api'; -import Layout from '@/components/layout/Layout'; import { Button } from '@/components/ui/button'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Dialog, DialogClose, DialogContent, DialogFooter, DialogTitle } from '@/components/ui/dialog'; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; -import { Separator } from '@/components/ui/separator'; import TagsSelectFormField from '@/components/ui/tag-selector'; import { toast } from '@/components/ui/use-toast'; import { useCurrentElectionRoundStore } from '@/context/election-round.store'; import { useMonitoringObserversTags } from '@/hooks/tags-queries'; import { zodResolver } from '@hookform/resolvers/zod'; import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { useNavigate } from '@tanstack/react-router'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; -import { MonitorObserverBackButton } from './MonitoringObserverBackButton'; -import { monitoringObserversKeys } from '../hooks/monitoring-observers-queries'; +import { monitoringObserversKeys } from '../../hooks/monitoring-observers-queries'; -export default function CreateMonitoringObserver() { +export interface CreateMonitoringObserverDialogProps { + open: boolean; + onOpenChange: (open: any) => void; +} + +function CreateMonitoringObserverDialog({ open, onOpenChange }: CreateMonitoringObserverDialogProps) { const { t } = useTranslation('translation', { keyPrefix: 'observers.addObserver' }); const currentElectionRoundId = useCurrentElectionRoundStore((s) => s.currentElectionRoundId); const { data: availableTags } = useMonitoringObserversTags(currentElectionRoundId); - const navigate = useNavigate(); const queryClient = useQueryClient(); const newObserverSchema = z.object({ firstName: z.string(), @@ -47,18 +47,15 @@ export default function CreateMonitoringObserver() { return authApi.post(`/election-rounds/${electionRoundId}/monitoring-observers`, { observers: [values] }); }, - onSuccess: (_, {electionRoundId}) => { + onSuccess: (_, { electionRoundId }) => { toast({ title: 'Success', description: t('onSuccess'), }); queryClient.invalidateQueries({ queryKey: monitoringObserversKeys.all(electionRoundId) }); - - navigate({ - to: '/monitoring-observers/$tab', - params: { tab: 'list' }, - }); + form.reset({}); + onOpenChange(false); }, onError: () => { toast({ @@ -68,16 +65,19 @@ export default function CreateMonitoringObserver() { }); }, }); + return ( - } enableBreadcrumbs={false}> - - -
- {t('title')} -
- -
- + + { + e.preventDefault(); + }} + onEscapeKeyDown={(e) => { + e.preventDefault(); + }}> + {t('title')} +
-
-
- - - -
-
+ + + - - - +
+
+
); } + +export default CreateMonitoringObserverDialog; diff --git a/web/src/features/monitoring-observers/components/MonitoringObserversList/MonitoringObserversList.tsx b/web/src/features/monitoring-observers/components/MonitoringObserversList/MonitoringObserversList.tsx index f806b18d4..c1a498074 100644 --- a/web/src/features/monitoring-observers/components/MonitoringObserversList/MonitoringObserversList.tsx +++ b/web/src/features/monitoring-observers/components/MonitoringObserversList/MonitoringObserversList.tsx @@ -16,7 +16,7 @@ import { Separator } from '@/components/ui/separator'; import { useDialog } from '@/components/ui/use-dialog'; import { Cog8ToothIcon, EllipsisVerticalIcon, FunnelIcon, PaperAirplaneIcon } from '@heroicons/react/24/outline'; import { useMutation } from '@tanstack/react-query'; -import { Link, useNavigate, useRouter } from '@tanstack/react-router'; +import { useNavigate, useRouter } from '@tanstack/react-router'; import { CellContext, ColumnDef } from '@tanstack/react-table'; import { useEffect, useMemo, useState } from 'react'; @@ -38,6 +38,7 @@ import { MonitoringObserver, MonitoringObserverStatus } from '../../models/monit import ImportMonitoringObserversDialog from '../MonitoringObserversList/ImportMonitoringObserversDialog'; import ImportMonitoringObserversErrorsDialog from '../MonitoringObserversList/ImportMonitoringObserversErrorsDialog'; import ConfirmResendInvitationDialog from './ConfirmResendInvitationDialog'; +import CreateMonitoringObserverDialog from './CreateMonitoringObserverDialog'; function MonitoringObserversList() { const navigate = useNavigate(); @@ -45,84 +46,85 @@ function MonitoringObserversList() { const search = Route.useSearch(); const currentElectionRoundId = useCurrentElectionRoundStore((s) => s.currentElectionRoundId); - const monitoringObserverColDefs: ColumnDef[] = useMemo(()=>{ + const monitoringObserverColDefs: ColumnDef[] = useMemo(() => { return [ - { - header: ({ column }) => , - accessorKey: 'name', - enableSorting: true, - enableGlobalFilter: true, - cell: ({ - row: { - original: { firstName, lastName }, - }, - }) => ( -

- {firstName} {lastName} -

- ), - }, - { - header: ({ column }) => , - accessorKey: 'email', - enableSorting: true, - }, - { - header: ({ column }) => , - accessorKey: 'tags', - cell: ({ - row: { - original: { tags }, - }, - }) => , - }, - { - header: ({ column }) => , - accessorKey: 'phoneNumber', - enableSorting: true, - }, - { - header: ({ column }) => , - accessorKey: 'status', - enableSorting: true, - cell: ({ - row: { - original: { status }, - }, - }) => {status}, - }, - { - header: ({ column }) => , - accessorKey: 'latestActivityAt', - enableSorting: true, - cell: ({ - row: { - original: { latestActivityAt }, - }, - }) =>

{latestActivityAt ? format(latestActivityAt, DateTimeFormat) : '-'}

, - }, - { - header: '', - accessorKey: 'action', - enableSorting: true, - cell: ({ row }) => ( - - - - - - navigateToObserver(row.original.id)}>View - navigateToEdit(row.original.id)}>Edit - handleResendInviteToObserver(row.original.id)}> - Resend invitation email - - - - ), - }, - ];}, [currentElectionRoundId]); + { + header: ({ column }) => , + accessorKey: 'name', + enableSorting: true, + enableGlobalFilter: true, + cell: ({ + row: { + original: { firstName, lastName }, + }, + }) => ( +

+ {firstName} {lastName} +

+ ), + }, + { + header: ({ column }) => , + accessorKey: 'email', + enableSorting: true, + }, + { + header: ({ column }) => , + accessorKey: 'tags', + cell: ({ + row: { + original: { tags }, + }, + }) => , + }, + { + header: ({ column }) => , + accessorKey: 'phoneNumber', + enableSorting: true, + }, + { + header: ({ column }) => , + accessorKey: 'status', + enableSorting: true, + cell: ({ + row: { + original: { status }, + }, + }) => {status}, + }, + { + header: ({ column }) => , + accessorKey: 'latestActivityAt', + enableSorting: true, + cell: ({ + row: { + original: { latestActivityAt }, + }, + }) =>

{latestActivityAt ? format(latestActivityAt, DateTimeFormat) : '-'}

, + }, + { + header: '', + accessorKey: 'action', + enableSorting: true, + cell: ({ row }) => ( + + + + + + navigateToObserver(row.original.id)}>View + navigateToEdit(row.original.id)}>Edit + handleResendInviteToObserver(row.original.id)}> + Resend invitation email + + + + ), + }, + ]; + }, [currentElectionRoundId]); const [searchText, setSearchText] = useState(search.searchText); const debouncedSearch = useDebounce(search, 300); @@ -130,6 +132,7 @@ function MonitoringObserversList() { const [importErrorsFileId, setImportErrorsFileId] = useState(); const [monitoringObserverId, setMonitoringObserverId] = useState(); + const createMonitoringObserverDialog = useDialog(); const importMonitoringObserversDialog = useDialog(); const importMonitoringObserverErrorsDialog = useDialog(); const confirmResendInvitesDialog = useDialog(); @@ -155,7 +158,7 @@ function MonitoringObserversList() { const params = [ ['status', debouncedSearch.monitoringObserverStatus], ['tags', debouncedSearch.tags], - ['searchText', debouncedSearch.searchText] + ['searchText', debouncedSearch.searchText], ].filter(([_, value]) => value); return Object.fromEntries(params); @@ -184,7 +187,7 @@ function MonitoringObserversList() { }); }, - onSuccess: (_, {electionRoundId}) => { + onSuccess: (_, { electionRoundId }) => { queryClient.invalidateQueries({ queryKey: monitoringObserversKeys.all(electionRoundId) }); router.invalidate(); @@ -251,19 +254,13 @@ function MonitoringObserversList() {
Monitoring observers list
- - - - {!!importErrorsFileId && ( )} + { @@ -291,6 +288,12 @@ function MonitoringObserversList() { Import observer list + + +