From 309c81cb0317f740da3421209ebd585434163fc8 Mon Sep 17 00:00:00 2001 From: r1anaf Date: Sun, 10 May 2026 14:35:31 -0400 Subject: [PATCH 1/5] work in progress --- .../src/components/ApplicationTable.tsx | 45 +++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/apps/frontend/src/components/ApplicationTable.tsx b/apps/frontend/src/components/ApplicationTable.tsx index b54caca81..1cc945668 100644 --- a/apps/frontend/src/components/ApplicationTable.tsx +++ b/apps/frontend/src/components/ApplicationTable.tsx @@ -1,5 +1,5 @@ -import { useMemo } from 'react'; -import { Table } from '@chakra-ui/react'; +import { useMemo, useState } from 'react'; +import { Button, Input, InputGroup, Table } from '@chakra-ui/react'; import type { ApplicationRow } from '@hooks/useApplications'; import StatusPill, { StatusPillConfig, StatusVariant } from './StatusPill'; import { @@ -8,6 +8,8 @@ import { EMPTY_APPLICATION_FILTERS, type ApplicationFilters, } from '@utils/applicationFilters'; +import apiClient from '@api/apiClient'; +import { MdEdit } from 'react-icons/md'; const COLUMNS = [ 'Name', @@ -62,11 +64,23 @@ function formatDesiredExperience(value: string): string { return value; } +interface isEditingType { + appId: number; + isBeingEdited: boolean; +} + export function ApplicationTable({ applications, searchQuery = '', filters = EMPTY_APPLICATION_FILTERS, }: ApplicationTableProps) { + const [applicationsState, setApplicationsState] = + useState(applications); + const [isEditing, setIsEditing] = useState( + applications.map( + (app) => ({ appId: app.appId, isBeingEdited: false } as isEditingType), + ), + ); const matchesStructuredFilters = useMemo( () => compileApplicationFilterPredicate(filters), [filters], @@ -88,6 +102,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 ( @@ -116,7 +143,19 @@ export function ApplicationTable({ {formatDate(application.proposedStartDate)} - {formatDate(application.actualStartDate)} + + handleActualStartDateUpdate('', application)} + /> + } + > + + + {formatDesiredExperience(application.desiredExperience)} From b8ea7c2de70d25fbd7ca38ca95db50f9de53343d Mon Sep 17 00:00:00 2001 From: Sam Nie <147653722+SamNie2027@users.noreply.github.com> Date: Tue, 12 May 2026 19:02:28 -0400 Subject: [PATCH 2/5] using the date input selector --- .../src/components/ApplicationTable.tsx | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/apps/frontend/src/components/ApplicationTable.tsx b/apps/frontend/src/components/ApplicationTable.tsx index 1cc945668..836eed9db 100644 --- a/apps/frontend/src/components/ApplicationTable.tsx +++ b/apps/frontend/src/components/ApplicationTable.tsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { Button, Input, InputGroup, Table } from '@chakra-ui/react'; import type { ApplicationRow } from '@hooks/useApplications'; import StatusPill, { StatusPillConfig, StatusVariant } from './StatusPill'; @@ -6,6 +6,7 @@ import { compileApplicationFilterPredicate, compileApplicationSearchPredicate, EMPTY_APPLICATION_FILTERS, + normalizeDateToDay, type ApplicationFilters, } from '@utils/applicationFilters'; import apiClient from '@api/apiClient'; @@ -76,6 +77,9 @@ export function ApplicationTable({ }: ApplicationTableProps) { const [applicationsState, setApplicationsState] = useState(applications); + const [actualStartDates, setActualStartDates] = useState< + Record + >({}); const [isEditing, setIsEditing] = useState( applications.map( (app) => ({ appId: app.appId, isBeingEdited: false } as isEditingType), @@ -91,6 +95,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) => { @@ -149,11 +166,27 @@ export function ApplicationTable({ flex="1" endElement={ handleActualStartDateUpdate('', application)} + onClick={() => + handleActualStartDateUpdate( + actualStartDates[application.appId] ?? '', + application, + ) + } /> } > - + { + const value = event.target.value; + setActualStartDates((prev) => ({ + ...prev, + [application.appId]: value, + })); + }} + /> From 278d256134eac45349fec058144c2ba12cff39cc Mon Sep 17 00:00:00 2001 From: r1anaf Date: Tue, 12 May 2026 20:24:13 -0400 Subject: [PATCH 3/5] finished editing toggle --- .../src/components/ApplicationTable.tsx | 99 ++++++++++--------- 1 file changed, 55 insertions(+), 44 deletions(-) diff --git a/apps/frontend/src/components/ApplicationTable.tsx b/apps/frontend/src/components/ApplicationTable.tsx index 836eed9db..b757f47c6 100644 --- a/apps/frontend/src/components/ApplicationTable.tsx +++ b/apps/frontend/src/components/ApplicationTable.tsx @@ -1,5 +1,12 @@ import { useEffect, useMemo, useState } from 'react'; -import { Button, Input, InputGroup, Table } from '@chakra-ui/react'; +import { + Button, + Flex, + Input, + InputGroup, + Spacer, + Table, +} from '@chakra-ui/react'; import type { ApplicationRow } from '@hooks/useApplications'; import StatusPill, { StatusPillConfig, StatusVariant } from './StatusPill'; import { @@ -11,6 +18,7 @@ import { } from '@utils/applicationFilters'; import apiClient from '@api/apiClient'; import { MdEdit } from 'react-icons/md'; +import { Navigate, useNavigate } from 'react-router-dom'; const COLUMNS = [ 'Name', @@ -65,26 +73,16 @@ function formatDesiredExperience(value: string): string { return value; } -interface isEditingType { - appId: number; - isBeingEdited: boolean; -} - export function ApplicationTable({ applications, searchQuery = '', filters = EMPTY_APPLICATION_FILTERS, }: ApplicationTableProps) { - const [applicationsState, setApplicationsState] = - useState(applications); + const navigate = useNavigate(); const [actualStartDates, setActualStartDates] = useState< Record >({}); - const [isEditing, setIsEditing] = useState( - applications.map( - (app) => ({ appId: app.appId, isBeingEdited: false } as isEditingType), - ), - ); + const [editingIds, setEditingIds] = useState>(new Set()); const matchesStructuredFilters = useMemo( () => compileApplicationFilterPredicate(filters), [filters], @@ -149,45 +147,58 @@ export function ApplicationTable({ {filteredApplications.map((application) => ( - - - - {titleCaseName(application.name)} - - + + navigate(`/admin/view-application/${application.appId}`) + } + transition="background-color 120ms ease-in-out" + _hover={{ backgroundColor: '#DBEAFE' }} + > + {titleCaseName(application.name)} {formatDate(application.proposedStartDate)} - + + {editingIds.has(application.appId) ? ( + + { + const value = event.target.value; + setActualStartDates((prev) => ({ + ...prev, + [application.appId]: value, + })); + }} + /> + + ) : ( + formatDate(actualStartDates[application.appId]) + )} + + { + 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), + ); } - /> - } - > - { - const value = event.target.value; - setActualStartDates((prev) => ({ - ...prev, - [application.appId]: value, - })); }} /> - + {formatDesiredExperience(application.desiredExperience)} From 553af87baa148f1ddd0be9804ae2779f62df1008 Mon Sep 17 00:00:00 2001 From: Sam Nie <147653722+SamNie2027@users.noreply.github.com> Date: Tue, 12 May 2026 21:00:11 -0400 Subject: [PATCH 4/5] No navigate when editing and better transition animation --- .../src/components/ApplicationTable.tsx | 176 ++++++++++-------- 1 file changed, 96 insertions(+), 80 deletions(-) diff --git a/apps/frontend/src/components/ApplicationTable.tsx b/apps/frontend/src/components/ApplicationTable.tsx index b757f47c6..2ee054630 100644 --- a/apps/frontend/src/components/ApplicationTable.tsx +++ b/apps/frontend/src/components/ApplicationTable.tsx @@ -1,12 +1,5 @@ import { useEffect, useMemo, useState } from 'react'; -import { - Button, - Flex, - Input, - InputGroup, - Spacer, - Table, -} from '@chakra-ui/react'; +import { Flex, Input, InputGroup, Spacer, Table } from '@chakra-ui/react'; import type { ApplicationRow } from '@hooks/useApplications'; import StatusPill, { StatusPillConfig, StatusVariant } from './StatusPill'; import { @@ -18,7 +11,7 @@ import { } from '@utils/applicationFilters'; import apiClient from '@api/apiClient'; import { MdEdit } from 'react-icons/md'; -import { Navigate, useNavigate } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; const COLUMNS = [ 'Name', @@ -146,79 +139,102 @@ export function ApplicationTable({ - {filteredApplications.map((application) => ( - - navigate(`/admin/view-application/${application.appId}`) - } - transition="background-color 120ms ease-in-out" - _hover={{ backgroundColor: '#DBEAFE' }} - > - {titleCaseName(application.name)} - {formatDate(application.proposedStartDate)} - - - {editingIds.has(application.appId) ? ( - - { - const value = event.target.value; - setActualStartDates((prev) => ({ - ...prev, - [application.appId]: value, - })); - }} - /> - - ) : ( - formatDate(actualStartDates[application.appId]) - )} - - { - if (editingIds.has(application.appId)) { - handleActualStartDateUpdate( - actualStartDates[application.appId] ?? '', - application, - ); + {filteredApplications.map((application) => { + const isEditingActualStart = editingIds.has(application.appId); - setEditingIds((prev) => { - const next = new Set(prev); - next.delete(application.appId); - return next; - }); - } else { - setEditingIds((prev) => - new Set(prev).add(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(); + } + }} + > + + {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 } - }} - /> - - - - {formatDesiredExperience(application.desiredExperience)} - - {application.applicantType} - {application.discipline} - - {titleCaseName(application.disciplineAdminName)} - - - {StatusPillConfig[application.status as StatusVariant] ? ( - - {StatusPillConfig[application.status as StatusVariant].label} - - ) : ( - application.status - )} - - - ))} + + ) : ( + application.status + )} + + + ); + })} ); From 32f578c47dfbf7c9ce7cb387349f3e471f8e5e3e Mon Sep 17 00:00:00 2001 From: Sam Nie <147653722+SamNie2027@users.noreply.github.com> Date: Fri, 22 May 2026 20:39:03 -0400 Subject: [PATCH 5/5] Fixing frontend tests failing --- apps/frontend/src/components/ApplicationTable.test.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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[] = [