diff --git a/apps/frontend/src/components/ApplicationTable.test.tsx b/apps/frontend/src/components/ApplicationTable.test.tsx index 6d6c3d7ca..4c406713f 100644 --- a/apps/frontend/src/components/ApplicationTable.test.tsx +++ b/apps/frontend/src/components/ApplicationTable.test.tsx @@ -1,6 +1,7 @@ import { render, screen } from '@testing-library/react'; import { describe, it, expect } from 'vitest'; import { ChakraProvider, defaultSystem } from '@chakra-ui/react'; +import { MemoryRouter } from 'react-router-dom'; import ApplicationTable from './ApplicationTable'; import type { ApplicationRow } from '@hooks/useApplications'; import { @@ -9,7 +10,11 @@ import { } from '@utils/applicationFilters'; function renderWithChakra(ui: React.ReactElement) { - return render({ui}); + return render( + + {ui} + , + ); } const mockApplications: ApplicationRow[] = [ diff --git a/apps/frontend/src/components/ApplicationTable.tsx b/apps/frontend/src/components/ApplicationTable.tsx index b54caca81..2ee054630 100644 --- a/apps/frontend/src/components/ApplicationTable.tsx +++ b/apps/frontend/src/components/ApplicationTable.tsx @@ -1,13 +1,17 @@ -import { useMemo } from 'react'; -import { Table } from '@chakra-ui/react'; +import { useEffect, useMemo, useState } from 'react'; +import { Flex, Input, InputGroup, Spacer, Table } from '@chakra-ui/react'; import type { ApplicationRow } from '@hooks/useApplications'; import StatusPill, { StatusPillConfig, StatusVariant } from './StatusPill'; import { compileApplicationFilterPredicate, compileApplicationSearchPredicate, EMPTY_APPLICATION_FILTERS, + normalizeDateToDay, type ApplicationFilters, } from '@utils/applicationFilters'; +import apiClient from '@api/apiClient'; +import { MdEdit } from 'react-icons/md'; +import { useNavigate } from 'react-router-dom'; const COLUMNS = [ 'Name', @@ -67,6 +71,11 @@ export function ApplicationTable({ searchQuery = '', filters = EMPTY_APPLICATION_FILTERS, }: ApplicationTableProps) { + const navigate = useNavigate(); + const [actualStartDates, setActualStartDates] = useState< + Record + >({}); + const [editingIds, setEditingIds] = useState>(new Set()); const matchesStructuredFilters = useMemo( () => compileApplicationFilterPredicate(filters), [filters], @@ -77,6 +86,19 @@ export function ApplicationTable({ [searchQuery], ); + useEffect(() => { + setActualStartDates((prev) => { + const next = { ...prev }; + applications.forEach((application) => { + if (next[application.appId] === undefined) { + next[application.appId] = + normalizeDateToDay(application.actualStartDate) ?? ''; + } + }); + return next; + }); + }, [applications]); + const filteredApplications = useMemo( () => applications.filter((application) => { @@ -88,6 +110,19 @@ export function ApplicationTable({ [applications, matchesSearchQuery, matchesStructuredFilters], ); + const handleActualStartDateUpdate = async ( + nextDate: string, + application: ApplicationRow, + ) => { + if (!application) return; + const updatedApplication = await apiClient.updateApplicationActualStartDate( + application.appId, + nextDate, + ); + // setApplicationsState(updatedApplication); TODO + console.log('we clicked the button!'); + }; + return ( @@ -104,38 +139,102 @@ export function ApplicationTable({ - {filteredApplications.map((application) => ( - - - { + const isEditingActualStart = editingIds.has(application.appId); + + return ( + + navigate(`/admin/view-application/${application.appId}`) + } + transition="background-color 240ms ease-in-out" + _hover={{ + backgroundColor: '#DBEAFE', + '& > td': { + backgroundColor: '#DBEAFE', + transition: 'background-color 240ms ease-in-out', + }, + }} + > + {titleCaseName(application.name)} + + {formatDate(application.proposedStartDate)} + + { + if (isEditingActualStart) { + event.stopPropagation(); + } + }} > - {titleCaseName(application.name)} - - - {formatDate(application.proposedStartDate)} - {formatDate(application.actualStartDate)} - - {formatDesiredExperience(application.desiredExperience)} - - {application.applicantType} - {application.discipline} - - {titleCaseName(application.disciplineAdminName)} - - - {StatusPillConfig[application.status as StatusVariant] ? ( - - {StatusPillConfig[application.status as StatusVariant].label} - - ) : ( - application.status - )} - - - ))} + + {isEditingActualStart ? ( + + { + event.stopPropagation(); + const value = event.target.value; + setActualStartDates((prev) => ({ + ...prev, + [application.appId]: value, + })); + }} + /> + + ) : ( + formatDate(actualStartDates[application.appId]) + )} + + { + event.stopPropagation(); + if (editingIds.has(application.appId)) { + handleActualStartDateUpdate( + actualStartDates[application.appId] ?? '', + application, + ); + + setEditingIds((prev) => { + const next = new Set(prev); + next.delete(application.appId); + return next; + }); + } else { + setEditingIds((prev) => + new Set(prev).add(application.appId), + ); + } + }} + /> + + + + {formatDesiredExperience(application.desiredExperience)} + + {application.applicantType} + {application.discipline} + + {titleCaseName(application.disciplineAdminName)} + + + {StatusPillConfig[application.status as StatusVariant] ? ( + + { + StatusPillConfig[application.status as StatusVariant] + .label + } + + ) : ( + application.status + )} + + + ); + })} );