From 9f30f43bb2954a937d3966c80ac73551d57f8d5a Mon Sep 17 00:00:00 2001 From: amrit Date: Fri, 10 Jul 2026 11:53:38 +0545 Subject: [PATCH] fix(links): add tab persist in table --- app/views/CapacityAndResources/index.tsx | 2 +- app/views/Links/LinkForm/index.tsx | 46 ++++++++++++++++++------ app/views/Links/index.tsx | 36 ++++++++++++------- backend | 2 +- 4 files changed, 60 insertions(+), 26 deletions(-) diff --git a/app/views/CapacityAndResources/index.tsx b/app/views/CapacityAndResources/index.tsx index dbce4cf..0372ccf 100644 --- a/app/views/CapacityAndResources/index.tsx +++ b/app/views/CapacityAndResources/index.tsx @@ -67,7 +67,7 @@ function CapacityAndResourcesList() { variables: { filters: { isActive: isDefined(filter.isActive) ? filter.isActive === 'true' : undefined, - title: filter.search ? { iContains: filter.search } : undefined, + search: filter.search || undefined, }, pagination: { limit, diff --git a/app/views/Links/LinkForm/index.tsx b/app/views/Links/LinkForm/index.tsx index bbb0d1d..fc6a9fe 100644 --- a/app/views/Links/LinkForm/index.tsx +++ b/app/views/Links/LinkForm/index.tsx @@ -1,8 +1,14 @@ import { useCallback, useEffect, + useMemo, } from 'react'; -import { useParams } from 'react-router'; +import { + createSearchParams, + useNavigate, + useParams, + useSearchParams, +} from 'react-router'; import { BlockLoading, Button, @@ -28,6 +34,7 @@ import { import { type LinkCreateInput, + LinkTypeEnum, type LinkUpdateInput, useCreateLinkMutation, useLinkDetailQuery, @@ -35,7 +42,7 @@ import { } from '#generated/types/graphql'; import useAlert from '#hooks/useAlert'; import useGlobalEnums from '#hooks/useGlobalEnums'; -import useRouting from '#hooks/useRouting'; +import routes from '#root/config/routes'; import { errorMessage, keySelector, @@ -64,12 +71,20 @@ const LinkSchema: FormSchema = { }), }; -const defaultEditFormValue: PartialFormType = {}; +function isAllowedLinkType(value: string | null | undefined): value is LinkTypeEnum { + return value === LinkTypeEnum.Internal || value === LinkTypeEnum.External; +} function LinkForm() { const { id } = useParams(); const alert = useAlert(); - const navigate = useRouting(); + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + + const seedType = searchParams.get('type'); + const initialValue = useMemo(() => ({ + linkType: isAllowedLinkType(seedType) ? seedType : undefined, + }), [seedType]); const [{ data, fetching: linkDetailFetch }] = useLinkDetailQuery({ variables: { id: isDefined(id) ? id : '' }, @@ -88,7 +103,7 @@ function LinkForm() { validate, setError, setValue, - } = useForm(LinkSchema, { value: defaultEditFormValue }); + } = useForm(LinkSchema, { value: initialValue }); const { linkType: linkTypeOptions, @@ -96,13 +111,22 @@ function LinkForm() { const error = getErrorObject(formError); + const navigateToLinks = useCallback((linkType?: LinkTypeEnum | null) => { + navigate({ + pathname: routes.links.path, + search: isDefined(linkType) + ? createSearchParams({ tab: linkType }).toString() + : undefined, + }); + }, [navigate]); + const handleCreate = useCallback(async (mutationData: PartialFormType) => { const createPayload = removeNull(mutationData) as unknown as LinkCreateInput; const res = await createLinkMutate({ data: createPayload }); const result = res.data?.createLink; if (isDefined(result) && result.ok) { - navigate('links'); + navigateToLinks(mutationData.linkType); alert.show('Link created successfully', { variant: 'success' }); } else if (isDefined(result) && isDefined(result.errors)) { setError(transformToFormError(result.errors)); @@ -110,7 +134,7 @@ function LinkForm() { } else { alert.show(errorMessage, { variant: 'danger' }); } - }, [createLinkMutate, navigate, alert, setError]); + }, [createLinkMutate, navigateToLinks, alert, setError]); const handleUpdate = useCallback(async (mutationData: PartialFormType) => { if (isNotDefined(id)) { @@ -124,7 +148,7 @@ function LinkForm() { const result = res.data?.updateLink; if (isDefined(result) && result.ok) { - navigate('links'); + navigateToLinks(mutationData.linkType); alert.show('Link updated successfully', { variant: 'success' }); } else if (isDefined(result) && isDefined(result.errors)) { setError(transformToFormError(result.errors)); @@ -132,7 +156,7 @@ function LinkForm() { } else { alert.show(errorMessage, { variant: 'danger' }); } - }, [updateLinkMutate, id, navigate, alert, setError]); + }, [updateLinkMutate, id, navigateToLinks, alert, setError]); const handleFormSubmit = useCallback( () => createSubmitHandler( @@ -144,8 +168,8 @@ function LinkForm() { ); const handleCancelClick = useCallback(() => { - navigate('links'); - }, [navigate]); + navigateToLinks(value.linkType); + }, [navigateToLinks, value.linkType]); useEffect(() => { if (isNotDefined(data?.link)) { diff --git a/app/views/Links/index.tsx b/app/views/Links/index.tsx index d142157..49a64da 100644 --- a/app/views/Links/index.tsx +++ b/app/views/Links/index.tsx @@ -1,8 +1,11 @@ import { useCallback, useMemo, - useState, } from 'react'; +import { + createSearchParams, + useNavigate, +} from 'react-router'; import { AddFillIcon } from '@ifrc-go/icons'; import { Button, @@ -34,7 +37,8 @@ import { } from '#generated/types/graphql'; import useAlert from '#hooks/useAlert'; import useFilterState from '#hooks/useFilterState'; -import useRouting from '#hooks/useRouting'; +import useUrlSearchState from '#hooks/useUrlSearchState'; +import routes from '#root/config/routes'; import { errorMessage, idSelector, @@ -42,19 +46,22 @@ import { type LinkListItem = NonNullable & { no: string } -interface LinkFilterType extends Omit { +interface LinkFilterType extends Omit { createdAtGte: string | undefined; createdAtLte: string | undefined; } const defaultFilter: LinkFilterType = { - linkType: LinkTypeEnum.Internal, search: undefined, createdAtGte: undefined, createdAtLte: undefined, }; function Links() { - const [activeTab, setActiveTab] = useState(LinkTypeEnum.Internal); + const [activeTab, setActiveTab] = useUrlSearchState( + 'tab', + (tab) => (tab as LinkTypeEnum) ?? LinkTypeEnum.Internal, + (tab) => tab, + ); const { filter, rawFilter, @@ -69,7 +76,7 @@ function Links() { }); const alert = useAlert(); - const navigate = useRouting(); + const navigate = useNavigate(); const queryVariables = useMemo(() => ({ pagination: { @@ -77,25 +84,25 @@ function Links() { offset, }, filters: { - linkType: filter.linkType, + linkType: activeTab, search: filter.search, createdAt: (filter.createdAtGte || filter.createdAtLte) ? { gte: filter.createdAtGte, lte: filter.createdAtLte, } : undefined, }, - }), [limit, offset, filter]); + }), [limit, offset, filter, activeTab]); const [{ fetching, data }, reExecuteQuery] = useLinksQuery( { variables: queryVariables }, ); const handleTabChanges = useCallback( - (name: LinkTypeEnum.Internal | LinkTypeEnum.External) => { + (name: LinkTypeEnum) => { setActiveTab(name); - setFilterField(name, 'linkType'); + setPage(1); }, - [setFilterField], + [setActiveTab, setPage], ); const [, deleteLink] = useDeleteLinkMutation(); @@ -163,8 +170,11 @@ function Links() { ], [onDeleteClick]); const handleCreateClick = useCallback(() => { - navigate('createLink'); - }, [navigate]); + navigate({ + pathname: routes.createLink.path, + search: createSearchParams({ type: activeTab }).toString(), + }); + }, [navigate, activeTab]); return (