diff --git a/.github/workflows/deploy-Dataspace.yml b/.github/workflows/deploy-Dataspace.yml index df3536f8..7f86bec2 100644 --- a/.github/workflows/deploy-Dataspace.yml +++ b/.github/workflows/deploy-Dataspace.yml @@ -54,6 +54,9 @@ jobs: BACKEND_GRAPHQL_URL: ${{ vars.BACKEND_GRAPHQL_URL }} NODE_ENV: production + - name: Lint + run: npm run lint + - name: Build run: npm run build diff --git a/.github/workflows/pre-merge.yml b/.github/workflows/pre-merge.yml index 1de59508..d9a5d359 100644 --- a/.github/workflows/pre-merge.yml +++ b/.github/workflows/pre-merge.yml @@ -47,5 +47,9 @@ jobs: run: npm run generate env: BACKEND_GRAPHQL_URL: ${{vars.BACKEND_GRAPHQL_URL}} + + - name: Lint + run: npm run lint + - name: Build application run: npm run build --if-present diff --git a/app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx b/app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx index 52b36f85..1b8bb95b 100644 --- a/app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx +++ b/app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx @@ -8,7 +8,6 @@ import { useQuery } from '@tanstack/react-query'; import { Button, Card, Icon, SearchInput, Select, Text } from 'opub-ui'; import { GraphQLPublic } from '@/lib/api'; -import { getCollaborativeDetailUrl } from '@/lib/collaborativesRouting'; import { cn, formatDate, generateJsonLd } from '@/lib/utils'; import BreadCrumbs from '@/components/BreadCrumbs'; import { Icons } from '@/components/icons'; diff --git a/app/[locale]/(user)/collaboratives/[collaborativeSlug]/page.tsx b/app/[locale]/(user)/collaboratives/[collaborativeSlug]/page.tsx index 2fc14eed..dfcd425f 100644 --- a/app/[locale]/(user)/collaboratives/[collaborativeSlug]/page.tsx +++ b/app/[locale]/(user)/collaboratives/[collaborativeSlug]/page.tsx @@ -55,7 +55,7 @@ export async function generateMetadata({ image: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/og.png`, }, }); - } catch (error) { + } catch { // Fallback to generic metadata if the API call fails return generatePageMetadata({ title: `Collaborative Details | CivicDataSpace`, diff --git a/app/[locale]/(user)/collaboratives/components/Details.tsx b/app/[locale]/(user)/collaboratives/components/Details.tsx index 8f20a25b..941411e9 100644 --- a/app/[locale]/(user)/collaboratives/components/Details.tsx +++ b/app/[locale]/(user)/collaboratives/components/Details.tsx @@ -44,7 +44,7 @@ const PrimaryDetails = ({ data, isLoading }: { data: any; isLoading: any }) => { - - - - - - ); -}; - const ChartCreateViz = ({ allDatasetsRes, params, diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/charts/components/ChartForm.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/charts/components/ChartForm.tsx index 72c1d099..66512d13 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/charts/components/ChartForm.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/charts/components/ChartForm.tsx @@ -38,6 +38,7 @@ const ChartForm: React.FC = ({ yAxisColumn: [{ fieldName: '', label: '', color: '#000000' }], }); } + // eslint-disable-next-line react-hooks/exhaustive-deps -- initialize yAxisColumn once when empty }, [chartData.options.yAxisColumn]); console.log(chartData); @@ -49,6 +50,7 @@ const ChartForm: React.FC = ({ filters: [{ column: '', operator: '==', value: '' }], }); } + // eslint-disable-next-line react-hooks/exhaustive-deps -- initialize filters once when empty }, [chartData.filters]); const handleYAxisColumnChange = ( diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/charts/components/ChartsImage.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/charts/components/ChartsImage.tsx index fcfe63b1..3dc31344 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/charts/components/ChartsImage.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/charts/components/ChartsImage.tsx @@ -181,7 +181,7 @@ const ChartsImage: React.FC = ({ } }, [chartImageDetails]); - const { mutate, isLoading: editMutationLoading } = useMutation( + const { isLoading: editMutationLoading } = useMutation( (data: { data: ResourceChartImageInputPartial }) => GraphQL(UpdateChartImageMutation, { [params.entityType]: params.entitySlug, @@ -206,21 +206,10 @@ const ChartsImage: React.FC = ({ })); }, []); - const onDrop = React.useCallback( - (_dropFiles: File[], acceptedFiles: File[]) => { - // mutate({ - // data: { - // id: imageId, - // image: acceptedFiles[0], - // }, - // }); - { - refetch(); - listrefetch(); - } - }, - [] - ); + const onDrop = React.useCallback(() => { + refetch(); + listrefetch(); + }, [listrefetch, refetch]); const handleSave = (updatedData: any) => { if (JSON.stringify(formData) !== JSON.stringify(previousFormData)) { @@ -244,7 +233,7 @@ const ChartsImage: React.FC = ({
+
{filteredRows.length > 0 ? ( diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/charts/components/ChartsVisualize.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/charts/components/ChartsVisualize.tsx index 56aa0315..d003f7bf 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/charts/components/ChartsVisualize.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/charts/components/ChartsVisualize.tsx @@ -110,7 +110,6 @@ const ChartsVisualize: React.FC = ({ const { data: chartsList, - isLoading, refetch: chartsListRefetch, }: { data: any; isLoading: boolean; refetch: any } = useQuery( [`chartsList_${params.id}`], @@ -209,7 +208,7 @@ const ChartsVisualize: React.FC = ({ refetch(); updateChartData(chartDetails.resourceChart); } - }, [chartId, chartDetails]); + }, [chartId, chartDetails, refetch]); const updateChartData = (resourceChart: any) => { if ( diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/contributors/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/contributors/page.tsx index be8fd911..cfaf4b59 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/contributors/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/contributors/page.tsx @@ -1,16 +1,16 @@ 'use client'; -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { useEffect, useState } from 'react'; import Image from 'next/image'; import { useParams } from 'next/navigation'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { Button, Icon, Text, toast } from 'opub-ui'; -import { useEffect, useState } from 'react'; +import { GraphQL } from '@/lib/api'; import { Icons } from '@/components/icons'; import { Loading } from '@/components/loading'; -import { GraphQL } from '@/lib/api'; -import { useEditStatus } from '../../context'; import CustomCombobox from '../../../../usecases/edit/[id]/contributors/CustomCombobox'; +import { useEditStatus } from '../../context'; import EntitySection from './EntitySelection'; import { AddContributors, @@ -25,7 +25,11 @@ import { } from './query'; const Details = () => { - const params = useParams<{ entityType: string; entitySlug: string; id: string }>(); + const params = useParams<{ + entityType: string; + entitySlug: string; + id: string; + }>(); const queryClient = useQueryClient(); const [searchValue, setSearchValue] = useState(''); const [formData, setFormData] = useState({ @@ -56,30 +60,36 @@ const Details = () => { ); const Organizations: { data: any; isLoading: boolean; refetch: any } = - useQuery([`fetch_orgs`], () => GraphQL(OrgList, { - [params.entityType]: params.entitySlug, - }, [])); - - - const CollaborativeData: { data: any; isLoading: boolean; refetch: any } = useQuery( - [`fetch_collaborative_${params.id}`], - () => + useQuery([`fetch_orgs`], () => GraphQL( - FetchCollaborativeInfo, + OrgList, { [params.entityType]: params.entitySlug, }, - { - filters: { - id: params.id, + [] + ) + ); + + const CollaborativeData: { data: any; isLoading: boolean; refetch: any } = + useQuery( + [`fetch_collaborative_${params.id}`], + () => + GraphQL( + FetchCollaborativeInfo, + { + [params.entityType]: params.entitySlug, }, - } - ), - { - refetchOnMount: true, - refetchOnReconnect: true, - } - ); + { + filters: { + id: params.id, + }, + } + ), + { + refetchOnMount: true, + refetchOnReconnect: true, + } + ); useEffect(() => { setFormData((prev) => ({ @@ -99,22 +109,30 @@ const Details = () => { }) ) || [], contributors: - CollaborativeData?.data?.collaboratives?.[0]?.contributors?.map((user: any) => ({ - label: user.fullName, - value: user.id, - })) || [], + CollaborativeData?.data?.collaboratives?.[0]?.contributors?.map( + (user: any) => ({ + label: user.fullName, + value: user.id, + }) + ) || [], })); }, [CollaborativeData?.data]); const { mutate: addContributor, isLoading: addContributorLoading } = useMutation( (input: { collaborativeId: string; userId: string }) => - GraphQL(AddContributors, { - [params.entityType]: params.entitySlug, - }, input), + GraphQL( + AddContributors, + { + [params.entityType]: params.entitySlug, + }, + input + ), { onSuccess: () => { - toast('Contributor added successfully', { id: COLLAB_CONTRIBUTORS_TOAST_ID }); + toast('Contributor added successfully', { + id: COLLAB_CONTRIBUTORS_TOAST_ID, + }); queryClient.invalidateQueries({ queryKey: [`fetch_collaborative_${params.id}`], }); @@ -128,7 +146,9 @@ const Details = () => { }); }, onError: (error: any) => { - toast(`Error: ${error.message}`, { id: COLLAB_CONTRIBUTORS_TOAST_ID }); + toast(`Error: ${error.message}`, { + id: COLLAB_CONTRIBUTORS_TOAST_ID, + }); }, } ); @@ -136,12 +156,18 @@ const Details = () => { const { mutate: removeContributor, isLoading: removeContributorLoading } = useMutation( (input: { collaborativeId: string; userId: string }) => - GraphQL(RemoveContributor, { - [params.entityType]: params.entitySlug, - }, input), + GraphQL( + RemoveContributor, + { + [params.entityType]: params.entitySlug, + }, + input + ), { onSuccess: () => { - toast('Contributor removed successfully', { id: COLLAB_CONTRIBUTORS_TOAST_ID }); + toast('Contributor removed successfully', { + id: COLLAB_CONTRIBUTORS_TOAST_ID, + }); queryClient.invalidateQueries({ queryKey: [`fetch_collaborative_${params.id}`], }); @@ -155,19 +181,27 @@ const Details = () => { }); }, onError: (error: any) => { - toast(`Error: ${error.message}`, { id: COLLAB_CONTRIBUTORS_TOAST_ID }); + toast(`Error: ${error.message}`, { + id: COLLAB_CONTRIBUTORS_TOAST_ID, + }); }, } ); const { mutate: addSupporter, isLoading: addSupporterLoading } = useMutation( (input: { collaborativeId: string; organizationId: string }) => - GraphQL(AddSupporters, { - [params.entityType]: params.entitySlug, - }, input), + GraphQL( + AddSupporters, + { + [params.entityType]: params.entitySlug, + }, + input + ), { onSuccess: () => { - toast('Supporter added successfully', { id: COLLAB_CONTRIBUTORS_TOAST_ID }); + toast('Supporter added successfully', { + id: COLLAB_CONTRIBUTORS_TOAST_ID, + }); queryClient.invalidateQueries({ queryKey: [`fetch_collaborative_${params.id}`], }); @@ -189,12 +223,18 @@ const Details = () => { const { mutate: removeSupporter, isLoading: removeSupporterLoading } = useMutation( (input: { collaborativeId: string; organizationId: string }) => - GraphQL(RemoveSupporters, { - [params.entityType]: params.entitySlug, - }, input), + GraphQL( + RemoveSupporters, + { + [params.entityType]: params.entitySlug, + }, + input + ), { onSuccess: () => { - toast('Supporter removed successfully', { id: COLLAB_CONTRIBUTORS_TOAST_ID }); + toast('Supporter removed successfully', { + id: COLLAB_CONTRIBUTORS_TOAST_ID, + }); queryClient.invalidateQueries({ queryKey: [`fetch_collaborative_${params.id}`], }); @@ -208,19 +248,27 @@ const Details = () => { }); }, onError: (error: any) => { - toast(`Error: ${error.message}`, { id: COLLAB_CONTRIBUTORS_TOAST_ID }); + toast(`Error: ${error.message}`, { + id: COLLAB_CONTRIBUTORS_TOAST_ID, + }); }, } ); const { mutate: addPartner, isLoading: addPartnerLoading } = useMutation( (input: { collaborativeId: string; organizationId: string }) => - GraphQL(AddPartners, { - [params.entityType]: params.entitySlug, - }, input), + GraphQL( + AddPartners, + { + [params.entityType]: params.entitySlug, + }, + input + ), { onSuccess: () => { - toast('Partner added successfully', { id: COLLAB_CONTRIBUTORS_TOAST_ID }); + toast('Partner added successfully', { + id: COLLAB_CONTRIBUTORS_TOAST_ID, + }); queryClient.invalidateQueries({ queryKey: [`fetch_collaborative_${params.id}`], }); @@ -242,12 +290,18 @@ const Details = () => { const { mutate: removePartner, isLoading: removePartnerLoading } = useMutation( (input: { collaborativeId: string; organizationId: string }) => - GraphQL(RemovePartners, { - [params.entityType]: params.entitySlug, - }, input), + GraphQL( + RemovePartners, + { + [params.entityType]: params.entitySlug, + }, + input + ), { onSuccess: () => { - toast('Partner removed successfully', { id: COLLAB_CONTRIBUTORS_TOAST_ID }); + toast('Partner removed successfully', { + id: COLLAB_CONTRIBUTORS_TOAST_ID, + }); queryClient.invalidateQueries({ queryKey: [`fetch_collaborative_${params.id}`], }); @@ -261,14 +315,16 @@ const Details = () => { }); }, onError: (error: any) => { - toast(`Error: ${error.message}`, { id: COLLAB_CONTRIBUTORS_TOAST_ID }); + toast(`Error: ${error.message}`, { + id: COLLAB_CONTRIBUTORS_TOAST_ID, + }); }, } ); useEffect(() => { Users.refetch(); - }, [searchValue]); + }, [searchValue, Users]); const selectedContributors = formData.contributors; @@ -280,18 +336,28 @@ const Details = () => { const { setStatus } = useEditStatus(); - const loadingStates = [ + useEffect(() => { + setStatus( + [ + addContributorLoading, + removeContributorLoading, + addSupporterLoading, + removeSupporterLoading, + addPartnerLoading, + removePartnerLoading, + ].some(Boolean) + ? 'loading' + : 'success' + ); + }, [ addContributorLoading, removeContributorLoading, addSupporterLoading, removeSupporterLoading, addPartnerLoading, removePartnerLoading, - ]; - - useEffect(() => { - setStatus(loadingStates.some(Boolean) ? 'loading' : 'success'); - }, loadingStates); + setStatus, + ]); return (
@@ -375,7 +441,7 @@ const Details = () => { }} kind="tertiary" > -
+
{item.label}
@@ -391,7 +457,10 @@ const Details = () => { title="SUPPORTED BY" label="Add Supporters" placeholder="Add Supporters" - data={CollaborativeData?.data?.collaboratives[0]?.supportingOrganizations} + data={ + CollaborativeData?.data?.collaboratives[0] + ?.supportingOrganizations + } options={(Organizations?.data?.allOrganizations || [])?.map( (org: any) => ({ label: org.name, @@ -432,7 +501,9 @@ const Details = () => { title="PARTNERED BY" label="Add Partners" placeholder="Add Partners" - data={CollaborativeData?.data?.collaboratives[0]?.partnerOrganizations} + data={ + CollaborativeData?.data?.collaboratives[0]?.partnerOrganizations + } options={(Organizations?.data?.allOrganizations || [])?.map( (org: any) => ({ label: org.name, diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/details/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/details/page.tsx index d7f01c53..b463fcfb 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/details/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/details/page.tsx @@ -1,11 +1,11 @@ 'use client'; import React, { useCallback, useEffect, useState } from 'react'; -import { useParams, useRouter } from 'next/navigation'; +import { useParams } from 'next/navigation'; import { graphql } from '@/gql'; import { CollaborativeInputPartial } from '@/gql/generated/graphql'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { DropZone, Select, Text, TextField, toast } from 'opub-ui'; +import { DropZone, Text, TextField, toast } from 'opub-ui'; import { GraphQL } from '@/lib/api'; import { RichTextEditor } from '@/components/RichTextEditor'; @@ -74,7 +74,6 @@ const Details = () => { id: string; }>(); - const router = useRouter(); const queryClient = useQueryClient(); const COLLAB_DETAILS_TOAST_ID = 'collaboratives-details-toast'; @@ -215,7 +214,7 @@ const Details = () => { }, }); }, - [] + [mutate, params.id] ); const onCoverImageDrop = React.useCallback( @@ -227,7 +226,7 @@ const Details = () => { }, }); }, - [] + [mutate, params.id] ); const handleSave = (updatedData: any) => { diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/metadata/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/metadata/page.tsx index becc12db..9114de06 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/metadata/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/metadata/page.tsx @@ -183,7 +183,7 @@ const Metadata = () => { } ); - const { data: metadataFields, isLoading: isMetadataFieldsLoading } = useQuery( + const { data: metadataFields } = useQuery( [`metadata_fields_COLLABORATIVE_${params.id}`], () => GraphQL( diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/publish/Details.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/publish/Details.tsx index 72ef6b66..fa22f37c 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/publish/Details.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/publish/Details.tsx @@ -9,11 +9,12 @@ import { RichTextRenderer } from '@/components/RichTextRenderer'; const Details = ({ data }: { data: any }) => { const [platformTitle, setPlatformTitle] = useState(null); const collaborative = data?.collaboratives?.[0]; + const platformUrl = collaborative?.platformUrl; useEffect(() => { const fetchTitle = async () => { try { - const urlItem = data.collaboratives[0].platformUrl; + const urlItem = collaborative?.platformUrl; if (urlItem && urlItem.value) { const title = await getWebsiteTitle(urlItem.value); @@ -24,12 +25,14 @@ const Details = ({ data }: { data: any }) => { } }; - if (data.collaboratives[0].platformUrl === null) { + if (!collaborative) return; + + if (platformUrl === null) { setPlatformTitle('N/A'); } else { fetchTitle(); } - }, [data?.collaboratives[0]?.platformUrl]); + }, [collaborative, platformUrl]); const PrimaryDetails = [ { label: 'Collaborative Name', value: collaborative?.title }, diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/publish/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/publish/page.tsx index 6e317d3e..551be315 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/publish/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/edit/[id]/publish/page.tsx @@ -169,7 +169,7 @@ const Publish = () => { { collaborativeId: params.id } ), { - onSuccess: (data: any) => { + onSuccess: () => { toast('Collaborative Published Successfully', { id: PUBLISH_SUCCESS_TOAST_ID, }); diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/page.tsx index 532006bd..4a84d60a 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/collaboratives/page.tsx @@ -100,7 +100,7 @@ export default function CollaborativePage() { if (isValidParams) { AllCollaboratives.refetch(); } - }, [navigationTab, isValidParams]); + }, [navigationTab, isValidParams, AllCollaboratives, setNavigationTab]); const COLLAB_LIST_TOAST_ID = 'collaboratives-list-toast'; diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/components/title-bar.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/components/title-bar.tsx index 0989e81b..22f6666e 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/components/title-bar.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/components/title-bar.tsx @@ -28,7 +28,7 @@ const TitleBar = ({ useEffect(() => { setStatus(loading ? 'loading' : 'success'); - }, [loading]); + }, [loading, setStatus]); return (
diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/components/AccessModelForm.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/components/AccessModelForm.tsx index fb3ffb35..d047e74a 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/components/AccessModelForm.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/components/AccessModelForm.tsx @@ -106,7 +106,7 @@ const AccessModelForm: React.FC = ({ useEffect(() => { setList(false); - }, []); + }, [setList]); const params = useParams<{ entityType: string; @@ -127,7 +127,6 @@ const AccessModelForm: React.FC = ({ const { data: accessModelList, - isLoading: accessModelListLoading, refetch: accessModelListRefetch, }: { data: any; isLoading: boolean; refetch: any } = useQuery( [`accessModelList_${params.id}`], @@ -143,7 +142,6 @@ const AccessModelForm: React.FC = ({ const { data: accessModelDetails, refetch: accessModelDetailsRefetch, - isLoading: accessModelDetailsLoading, }: { data: any; isLoading: boolean; refetch: any } = useQuery( [`accessModelDetails${params.id}`], () => @@ -168,7 +166,7 @@ const AccessModelForm: React.FC = ({ useState(accessModelData); const [selectedResources, setSelectedResources] = useState([]); - const [showSelectAll, setShowSelectAll] = useState(false); + const [, setShowSelectAll] = useState(false); const [availableResources, setAvailableResources] = useState< { label: string; value: string; schema: [] }[] @@ -234,7 +232,7 @@ const AccessModelForm: React.FC = ({ })); setSelectedFields(selectedFieldsIds); } - }, [accessModelDetails, accessModelId]); + }, [accessModelDetails, accessModelId, accessModelDetailsRefetch, params.id]); const handleAddResource = (resourceDetails: any) => { setSelectedResources(resourceDetails); @@ -371,7 +369,7 @@ const AccessModelForm: React.FC = ({
+
; mutate: any; diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/components/EditLayout.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/components/EditLayout.tsx index a7a8c891..c06555ce 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/components/EditLayout.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/components/EditLayout.tsx @@ -70,7 +70,7 @@ export function EditLayout({ children, params }: LayoutProps) { id: string; }>(); - const [editMode, setEditMode] = useState(false); + const [, setEditMode] = useState(false); const getDatasetTitleRes: { data: any; isLoading: boolean; refetch: any } = useQuery([`dataset_title_${routerParams.id}`], () => @@ -97,7 +97,7 @@ export function EditLayout({ children, params }: LayoutProps) { data ), { - onSuccess: (data: any) => { + onSuccess: () => { // queryClient.invalidateQueries({ // queryKey: [`create_dataset_${'52'}`], // }); diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/components/EditMetadata.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/components/EditMetadata.tsx index 3e2abbd7..2520816c 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/components/EditMetadata.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/components/EditMetadata.tsx @@ -666,24 +666,26 @@ export function EditMetadata({ id }: { id: string }) { updateMetadataMutation.mutate({ UpdateMetadataInput: updateInput }); }; - const handleSaveAsync = async (updatedData: any) => { - const updateInput = getUpdateInput(updatedData); - if (!updateInput) return; - - await updateMetadataMutation.mutateAsync({ - UpdateMetadataInput: updateInput, - }); - }; - const { setStatus, registerBeforeNavigateHandler } = useDatasetEditStatus(); useEffect(() => { + const handleSaveAsync = async (updatedData: any) => { + const updateInput = getUpdateInput(updatedData); + if (!updateInput) return; + + await updateMetadataMutation.mutateAsync({ + UpdateMetadataInput: updateInput, + }); + }; + registerBeforeNavigateHandler(() => handleSaveAsync(formDataRef.current)); return () => { registerBeforeNavigateHandler(null); }; - }, [previousFormData, registerBeforeNavigateHandler]); + // getUpdateInput reads previousFormData; formDataRef is read at handler invocation time + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [registerBeforeNavigateHandler, updateMetadataMutation]); function renderInputField(metadataFormItem: any) { if (metadataFormItem.dataType === 'STRING') { @@ -810,7 +812,7 @@ export function EditMetadata({ id }: { id: string }) { useEffect(() => { setStatus(updateMetadataMutation.isLoading ? 'loading' : 'success'); // update based on mutation state - }, [updateMetadataMutation.isLoading]); + }, [updateMetadataMutation.isLoading, setStatus]); return ( <> @@ -1075,7 +1077,7 @@ export function EditMetadata({ id }: { id: string }) { handleChange('accessType', 'PUBLIC')} + onChange={() => handleChange('accessType', 'PUBLIC')} >
Open Access diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/metadata/page-layout.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/metadata/page-layout.tsx index dcf2a8a1..6f7c0446 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/metadata/page-layout.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/metadata/page-layout.tsx @@ -1,104 +1,7 @@ 'use client'; import React from 'react'; -import { graphql } from '@/gql'; -// import { UpdateDatasetInput } from '@/gql/generated/graphql'; -import { usePRouter } from '@/hooks/use-prouter'; -import { useQuery } from '@tanstack/react-query'; - -import { GraphQL } from '@/lib/api'; -import { EditMetadata } from '../components/EditMetadata'; - -// const datasetQueryDoc = graphql(` -// query datasetQuery($dataset_id: Int) { -// dataset(dataset_id: $dataset_id) { -// id -// title -// description -// source -// update_frequency -// language -// remote_issued -// geography { -// name -// id -// } -// tags { -// id -// name -// } -// } -// } -// `); - -// const updateDatasetMutationDoc = graphql(` -// mutation updateDatasetMutation($dataset_data: UpdateDatasetInput) { -// update_dataset(dataset_data: $dataset_data) { -// success -// errors -// dataset { -// id -// title -// description -// remote_issued -// update_frequency -// geography { -// name -// id -// } -// source -// tags { -// id -// name -// } -// } -// } -// } -// `); export function MetadataPage({ params }: { params: { id: string } }) { - // const router = usePRouter(); - // const submitRef = React.useRef(null); - - // const { data } = useQuery([`dataset_meta_${params.id}`], () => - // GraphQL(datasetQueryDoc, { dataset_id: Number(params.id) }) - // ); - - // const queryClient = useQueryClient(); - // const { mutate, isLoading } = useMutation( - // (data: { dataset_data: UpdateDatasetInput }) => - // GraphQL(updateDatasetMutationDoc, data), - // { - // onSuccess: (data) => { - // queryClient.invalidateQueries({ - // queryKey: [`dataset_meta_${params.id}`], - // }); - // router.push( - // `/dashboard/dataset/${data.update_dataset?.dataset?.id}/edit/resources` - // ); - // }, - // } - // ); - - return ( - <> - {/* geo.name) || [], - update_frequency: data?.dataset?.update_frequency || '', - tags_list: data?.dataset?.tags?.map((tag) => tag.name) || [], - remote_issued: data?.dataset?.remote_issued || '', - source: data?.dataset?.source || '', - sector_list: [], - }} - isLoading={isLoading} - mutate={mutate} - /> */} - Edit Metadata - - ); + return <>Edit Metadata: {params.id}; } diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/page-layout.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/page-layout.tsx index df71a20c..809235fb 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/page-layout.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/page-layout.tsx @@ -1,106 +1,7 @@ 'use client'; import React from 'react'; -import { graphql } from '@/gql'; -// import { PatchDatasetInput } from '@/gql/generated/graphql'; -import { usePRouter } from '@/hooks/use-prouter'; -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; - -import { GraphQL } from '@/lib/api'; -import { loadingStart } from '@/lib/navigation'; -import { ActionBar } from '../../components/action-bar'; -import { EditDataset } from './components/EditDataset'; - -// const datasetQueryDoc = graphql(` -// query datasetEditQuery($dataset_id: Int) { -// dataset(dataset_id: $dataset_id) { -// id -// title -// description -// } -// } -// `); - -// const patchDatasetMutationDoc = graphql(` -// mutation patchDatasetMutation($dataset_data: PatchDatasetInput) { -// patch_dataset(dataset_data: $dataset_data) { -// success -// errors -// dataset { -// id -// title -// description -// } -// } -// } -// `); export function EditPage({ params }: { params: { id: string } }) { - const router = usePRouter(); - const submitRef = React.useRef(null); - - // const { data } = useQuery([`dataset_${params.id}`], () => - // GraphQL(datasetQueryDoc, { - // dataset_id: Number(params.id), - // }) - // ); - - const queryClient = useQueryClient(); - // const { mutate, isLoading } = useMutation( - // (data: { dataset_data: PatchDatasetInput }) => - // GraphQL(patchDatasetMutationDoc, data), - // { - // onSuccess: (data) => { - // queryClient.invalidateQueries({ - // queryKey: [`dataset_${params.id}`], - // }); - // queryClient.invalidateQueries({ - // queryKey: [`dataset_layout_${params.id}`], - // }); - // router.push( - // `/dashboard/dataset/${data.patch_dataset?.dataset?.id}/edit/metadata` - // ); - // }, - // } - // ); - - // React.useEffect(() => { - // router.prefetch(`/dashboard/dataset/${params.id}/edit/metadata`); - // }, []); - - return ( - <> - {/* { - loadingStart(); - submitRef.current?.click(); - }, - }} - secondaryAction={{ - content: 'Cancel', - onAction: () => router.push('/dashboard/dataset'), - }} - previousPage={{ - link: `/dashboard/dataset`, - content: 'My Datasets', - }} - isLoading={isLoading} - /> - */} - - ); + return <>Edit Dataset: {params.id}; } diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/publish/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/publish/page.tsx index ecc39844..0f51464f 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/publish/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/publish/page.tsx @@ -278,7 +278,7 @@ const Page = () => { { datasetId: params.id } ), { - onSuccess: (data: any) => { + onSuccess: () => { toast('Dataset Published Successfully', { id: PUBLISH_SUCCESS_TOAST_ID, }); diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/EditResource.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/EditResource.tsx index 1c392fe2..ac8c1b13 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/EditResource.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/EditResource.tsx @@ -269,7 +269,7 @@ export const EditResource = ({ data ), { - onSuccess: (data, variables) => { + onSuccess: () => { toast('File changes saved', { action: { label: 'Dismiss', @@ -458,7 +458,7 @@ export const EditResource = ({ useEffect(() => { resourceDetailsQuery.refetch(); - }, []); + }, [resourceDetailsQuery]); React.useEffect(() => { const ResourceData = resourceDetailsQuery.data?.resourceById; @@ -508,7 +508,7 @@ export const EditResource = ({ setFile((files) => [...files, ...acceptedFiles]); setIsSheetOpen(false); }, - [] + [createResourceMutation, params.id] ); const uploadedFile = file.length > 0 && ( @@ -538,7 +538,7 @@ export const EditResource = ({ } ); }, - [resourceId] + [resourceId, updateResourceMutation, resourceDetailsQuery] ); const fileInput = ( @@ -556,24 +556,6 @@ export const EditResource = ({ setResourceId(''); }; - const handlePreviewDetailsChange = ( - field: string, - value: string | boolean - ) => { - if (field === 'isAllEntries' && value) { - setPreviewDetails({ - startEntry: 0, - endEntry: 0, - isAllEntries: false, - }); - } else { - setPreviewDetails((prev) => ({ - ...prev, - [field]: value, - })); - } - }; - const saveResource = () => { updateResourceMutation.mutate({ fileResourceInput: { diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/ResourceDropzone.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/ResourceDropzone.tsx index 64be1572..5022901b 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/ResourceDropzone.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/ResourceDropzone.tsx @@ -25,9 +25,9 @@ export const ResourceDropzone = ({ reload }: { reload: () => void }) => { }>(); const [file, setFile] = React.useState([]); - const [resourceId, setResourceId] = useQueryState('id', parseAsString); + const [, setResourceId] = useQueryState('id', parseAsString); - const { mutate, isLoading } = useMutation( + const { mutate } = useMutation( (data: { fileResourceInput: CreateFileResourceInput }) => GraphQL( createResourceFilesDoc, @@ -60,7 +60,7 @@ export const ResourceDropzone = ({ reload }: { reload: () => void }) => { }); setFile((files) => [...files, ...acceptedFiles]); }, - [] + [mutate, params.id] ); const hint = ( diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/ResourceHeader.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/ResourceHeader.tsx index c057c616..2e927bac 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/ResourceHeader.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/ResourceHeader.tsx @@ -86,7 +86,7 @@ const ResourceHeader = ({ kind={'tertiary'} className="flex w-full justify-start" disabled={resourceId === item.value} - onClick={(e) => { + onClick={() => { handleResourceChange(item.value); setIsSheetOpen(false); }} diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/ResourceListView.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/ResourceListView.tsx index 0c62a0af..a8b28445 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/ResourceListView.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/[id]/edit/resources/components/ResourceListView.tsx @@ -57,7 +57,7 @@ export const ResourceListView = ({ data, refetch, isPromptDataset = false }: Res useEffect(() => { refetch(); - }, [resourceId]); + }, [resourceId, refetch]); const updateResourceMutation = useMutation( (data: { resourceId: string }) => @@ -239,7 +239,7 @@ export const ResourceListView = ({ data, refetch, isPromptDataset = false }: Res }); setFile((files) => [...files, ...acceptedFiles]); }, - [] + [createResourceMutation, params.id] ); const uploadedFile = file.length > 0 && ( diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/page.tsx index 42d13e26..86bd1a3d 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/dataset/page.tsx @@ -109,7 +109,7 @@ export default function DatasetPage() { if (isValidParams) { AllDatasetsQuery.refetch(); } - }, [navigationTab, isValidParams]); + }, [navigationTab, isValidParams, AllDatasetsQuery, setNavigationTab]); const DeleteDatasetMutation: { mutate: any; diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/layout.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/layout.tsx index 862d89fb..7398763d 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/layout.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/layout.tsx @@ -41,14 +41,14 @@ export default function OrgDashboardLayout({ children }: DashboardLayoutProps) { useEffect(() => { EntityDetailsQryRes.refetch(); - }, []); + }, [EntityDetailsQryRes]); useEffect(() => { if (EntityDetailsQryRes.data) { setEntityDetails(EntityDetailsQryRes.data); } - }, [EntityDetailsQryRes.data]); + }, [EntityDetailsQryRes.data, setEntityDetails]); if ( process.env.NEXT_PUBLIC_DATASPACE_FEATURE_ENABLED !== 'true' && diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/profile/orgProfile.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/profile/orgProfile.tsx index 2945f174..b0415ea1 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/profile/orgProfile.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/profile/orgProfile.tsx @@ -61,7 +61,7 @@ const OrgProfile = () => { location: entityDetails?.organizations[0].location, }); } - }, [entityDetails?.organizations]); + }, [entityDetails]); const initialFormData = { name: '', diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/assign/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/assign/page.tsx index 25351dcc..66e1c939 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/assign/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/assign/page.tsx @@ -4,7 +4,7 @@ import React, { useEffect, useState } from 'react'; import { useParams, useRouter } from 'next/navigation'; import { fetchDatasets } from '@/fetch'; import { graphql } from '@/gql'; -import { useMutation, useQuery } from '@tanstack/react-query'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { Button, DataTable, Text, toast } from 'opub-ui'; import { GraphQL } from '@/lib/api'; @@ -54,6 +54,7 @@ const Assign = () => { ? error.message.trim() : fallback; const router = useRouter(); + const queryClient = useQueryClient(); const [data, setData] = useState([]); // Ensure `data` is an array const [selectedRow, setSelectedRows] = useState([]); @@ -136,6 +137,14 @@ const Assign = () => { id: USECASE_ASSIGN_SUCCESS_TOAST_ID, }); UseCaseDetails.refetch(); + queryClient.invalidateQueries({ + queryKey: [ + `fetch_UsecaseDetails`, + params.id, + params.entityType, + params.entitySlug, + ], + }); router.push( `/dashboard/${params.entityType}/${params.entitySlug}/usecases/edit/${params.id}/dashboards` ); diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/contributors/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/contributors/page.tsx index 6b067421..882491cf 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/contributors/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/contributors/page.tsx @@ -130,6 +130,24 @@ const Details = () => { }, [UseCaseData?.data]); const queryClient = useQueryClient(); + const invalidateUseCaseQueries = () => { + queryClient.invalidateQueries({ + queryKey: [ + `fetch_usecase`, + params.id, + params.entityType, + params.entitySlug, + ], + }); + queryClient.invalidateQueries({ + queryKey: [ + `fetch_UsecaseDetails`, + params.id, + params.entityType, + params.entitySlug, + ], + }); + }; const { mutate: addContributor, isLoading: addContributorLoading } = useMutation( @@ -146,14 +164,7 @@ const Details = () => { toast('Contributor added successfully', { id: CONTRIBUTORS_ADD_SUCCESS_TOAST_ID, }); - queryClient.invalidateQueries({ - queryKey: [ - `fetch_usecase`, - params.id, - params.entityType, - params.entitySlug, - ], - }); + invalidateUseCaseQueries(); }, onError: (error: any) => { toast( @@ -179,14 +190,7 @@ const Details = () => { toast('Contributor removed successfully', { id: CONTRIBUTORS_REMOVE_SUCCESS_TOAST_ID, }); - queryClient.invalidateQueries({ - queryKey: [ - `fetch_usecase`, - params.id, - params.entityType, - params.entitySlug, - ], - }); + invalidateUseCaseQueries(); }, onError: (error: any) => { toast( @@ -211,14 +215,7 @@ const Details = () => { toast('Supporter added successfully', { id: SUPPORTER_ADD_SUCCESS_TOAST_ID, }); - queryClient.invalidateQueries({ - queryKey: [ - `fetch_usecase`, - params.id, - params.entityType, - params.entitySlug, - ], - }); + invalidateUseCaseQueries(); }, onError: (error: any) => { toast( @@ -244,14 +241,7 @@ const Details = () => { toast('Supporter removed successfully', { id: SUPPORTER_REMOVE_SUCCESS_TOAST_ID, }); - queryClient.invalidateQueries({ - queryKey: [ - `fetch_usecase`, - params.id, - params.entityType, - params.entitySlug, - ], - }); + invalidateUseCaseQueries(); }, onError: (error: any) => { toast( @@ -276,14 +266,7 @@ const Details = () => { toast('Partner added successfully', { id: PARTNER_ADD_SUCCESS_TOAST_ID, }); - queryClient.invalidateQueries({ - queryKey: [ - `fetch_usecase`, - params.id, - params.entityType, - params.entitySlug, - ], - }); + invalidateUseCaseQueries(); }, onError: (error: any) => { toast( @@ -309,14 +292,7 @@ const Details = () => { toast('Partner removed successfully', { id: PARTNER_REMOVE_SUCCESS_TOAST_ID, }); - queryClient.invalidateQueries({ - queryKey: [ - `fetch_usecase`, - params.id, - params.entityType, - params.entitySlug, - ], - }); + invalidateUseCaseQueries(); }, onError: (error: any) => { toast( @@ -329,7 +305,7 @@ const Details = () => { useEffect(() => { Users.refetch(); - }, [searchValue]); + }, [searchValue, Users]); const selectedContributors = formData.contributors; @@ -341,18 +317,28 @@ const Details = () => { const { setStatus } = useEditStatus(); - const loadingStates = [ + useEffect(() => { + setStatus( + [ + addContributorLoading, + removeContributorLoading, + addSupporterLoading, + removeSupporterLoading, + addPartnerLoading, + removePartnerLoading, + ].some(Boolean) + ? 'loading' + : 'success' + ); + }, [ addContributorLoading, removeContributorLoading, addSupporterLoading, removeSupporterLoading, addPartnerLoading, removePartnerLoading, - ]; - - useEffect(() => { - setStatus(loadingStates.some(Boolean) ? 'loading' : 'success'); - }, loadingStates); + setStatus, + ]); return (
diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/dashboards/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/dashboards/page.tsx index 26c89c03..ef832f1f 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/dashboards/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/dashboards/page.tsx @@ -142,13 +142,24 @@ const Dashboard = () => { }, [dashboardData]); const queryClient = useQueryClient(); + const invalidateDashboardQueries = () => { + queryClient.invalidateQueries({ queryKey }); + queryClient.invalidateQueries({ + queryKey: [ + `fetch_UsecaseDetails`, + params.id, + params.entityType, + params.entitySlug, + ], + }); + }; const { mutate: addDashboard, isLoading: addLoading } = useMutation( ({ usecaseId }: { usecaseId: number }) => GraphQL(AddDashboard, ownerArgs || {}, { usecaseId }), { onSuccess: () => { - queryClient.invalidateQueries({ queryKey }); + invalidateDashboardQueries(); toast.success('Dashboard added', { id: DASHBOARD_ADD_SUCCESS_TOAST_ID, }); @@ -160,7 +171,7 @@ const Dashboard = () => { GraphQL(updateDashboard, ownerArgs || {}, { id, name, link }), { onSuccess: ({ updateUsecaseDashboard }: any) => { - queryClient.invalidateQueries({ queryKey }); + invalidateDashboardQueries(); toast.success('Changes saved', { id: DASHBOARD_SAVE_SUCCESS_TOAST_ID }); setPreviousState((prev: any) => ({ ...prev, @@ -183,7 +194,7 @@ const Dashboard = () => { toast.success('Dashboard deleted', { id: DASHBOARD_DELETE_SUCCESS_TOAST_ID, }); - queryClient.invalidateQueries({ queryKey }); + invalidateDashboardQueries(); }, onError: (error: any) => { toast( diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/details/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/details/page.tsx index 04bbe418..30063676 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/details/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/details/page.tsx @@ -138,7 +138,7 @@ const Details = () => { const [formData, setFormData] = useState(initialFormData); - const [previousFormData, setPreviousFormData] = useState(initialFormData); + const [, setPreviousFormData] = useState(initialFormData); useEffect(() => { if (UsecasesData) { @@ -193,6 +193,14 @@ const Details = () => { params.entitySlug, ], }); + queryClient.invalidateQueries({ + queryKey: [ + `fetch_UsecaseDetails`, + params.id, + params.entityType, + params.entitySlug, + ], + }); }, onError: (error: any) => { toast( @@ -219,7 +227,7 @@ const Details = () => { }, }); }, - [] + [mutate, params.id] ); const handleSave = (updatedData: any) => { @@ -250,7 +258,7 @@ const Details = () => { useEffect(() => { setStatus(editMutationLoading ? 'loading' : 'success'); // update based on mutation state - }, [editMutationLoading]); + }, [editMutationLoading, setStatus]); return (
diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/metadata/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/metadata/page.tsx index b199af93..04fbc310 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/metadata/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/metadata/page.tsx @@ -275,7 +275,7 @@ const Metadata = () => { const [formData, setFormData] = useState( defaultValuesPrepFn(useCaseData?.data?.useCases?.[0] || {}) ); - const [previousFormData, setPreviousFormData] = useState(formData); + const [, setPreviousFormData] = useState(formData); const getSectorsList: { data: any; isLoading: boolean; error: any } = useQuery([`sectors_list_query`], () => @@ -359,6 +359,14 @@ const Metadata = () => { params.entitySlug, ], }); + queryClient.invalidateQueries({ + queryKey: [ + `fetch_UsecaseDetails`, + params.id, + params.entityType, + params.entitySlug, + ], + }); }, onError: (error: any) => { toast( @@ -371,7 +379,7 @@ const Metadata = () => { useEffect(() => { setStatus(updateUseCase.isLoading ? 'loading' : 'success'); // update based on mutation state - }, [updateUseCase.isLoading]); + }, [updateUseCase.isLoading, setStatus]); useEffect(() => { if (useCaseData.data?.useCases?.[0]) { diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/publish/Details.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/publish/Details.tsx index e20d01fe..5c91b2d7 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/publish/Details.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/publish/Details.tsx @@ -8,11 +8,13 @@ import { RichTextRenderer } from '@/components/RichTextRenderer'; const Details = ({ data }: { data: any }) => { const [platformTitle, setPlatformTitle] = useState(null); + const useCase = data?.useCases?.[0]; + const platformUrl = useCase?.platformUrl; useEffect(() => { const fetchTitle = async () => { try { - const urlItem = data.useCases[0].platformUrl; + const urlItem = useCase?.platformUrl; if (urlItem && urlItem.value) { const title = await getWebsiteTitle(urlItem.value); @@ -23,12 +25,14 @@ const Details = ({ data }: { data: any }) => { } }; - if (data.useCases[0].platformUrl === null) { + if (!useCase) return; + + if (platformUrl === null) { setPlatformTitle('N/A'); } else { fetchTitle(); } - }, [data?.useCases, data?.useCases[0]?.platformUrl]); + }, [useCase, platformUrl]); const PrimaryDetails = [ { label: 'Use Case Name', value: data?.useCases[0]?.title }, diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/publish/page.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/publish/page.tsx index 8a00adc9..b5ce1773 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/publish/page.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/[id]/publish/page.tsx @@ -128,7 +128,7 @@ const Publish = () => { id: string; }>(); const UseCaseData: { data: any; isLoading: boolean; refetch: any } = useQuery( - [`fetch_UsecaseDetails`], + [`fetch_UsecaseDetails`, params.id, params.entityType, params.entitySlug], () => GraphQL( UseCaseDetails, @@ -142,8 +142,8 @@ const Publish = () => { } ), { - refetchOnMount: true, - refetchOnReconnect: true, + refetchOnMount: 'always', + refetchOnReconnect: 'always', } ); const router = useRouter(); diff --git a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/layout.tsx b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/layout.tsx index cfdb5a48..8ad48330 100644 --- a/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/layout.tsx +++ b/app/[locale]/dashboard/[entityType]/[entitySlug]/usecases/edit/layout.tsx @@ -3,7 +3,7 @@ import { useParams, usePathname, useRouter } from 'next/navigation'; import { graphql } from '@/gql'; import { UseCaseInputPartial } from '@/gql/generated/graphql'; -import { useMutation, useQuery } from '@tanstack/react-query'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { Tab, TabList, Tabs, toast } from 'opub-ui'; import { GraphQL } from '@/lib/api'; @@ -40,6 +40,7 @@ const TabsAndChildren = ({ children }: { children: React.ReactNode }) => { }>(); const USECASE_TITLE_SUCCESS_TOAST_ID = 'usecase-title-save-success'; const USECASE_TITLE_ERROR_TOAST_ID = 'usecase-title-save-error'; + const queryClient = useQueryClient(); const getErrorMessage = (error: any, fallback: string) => typeof error?.message === 'string' && error.message.trim() ? error.message.trim() @@ -89,6 +90,14 @@ const TabsAndChildren = ({ children }: { children: React.ReactNode }) => { }); // Optionally, reset form or perform other actions UseCaseData.refetch(); + queryClient.invalidateQueries({ + queryKey: [ + `fetch_UsecaseDetails`, + params.id, + params.entityType, + params.entitySlug, + ], + }); }, onError: (error: any) => { toast( diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index 03d78ecb..bc6a24a9 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -74,7 +74,7 @@ export default async function LocaleLayout({ let messages; try { messages = (await import(`../../locales/${locale}.json`)).default; - } catch (error) { + } catch { notFound(); } setRequestLocale(locale); diff --git a/components/RichTextEditor/RichTextEditor.tsx b/components/RichTextEditor/RichTextEditor.tsx index cd55daf9..63f4f9f8 100644 --- a/components/RichTextEditor/RichTextEditor.tsx +++ b/components/RichTextEditor/RichTextEditor.tsx @@ -23,7 +23,6 @@ const RichTextEditor: React.FC = ({ label, helpText, readOnly = false, - showPreview = true, }) => { const [isPreview, setIsPreview] = useState(false); // Dynamically import ReactQuill to avoid SSR issues diff --git a/hooks/use-key-press.ts b/hooks/use-key-press.ts index f5ba306c..917acc64 100644 --- a/hooks/use-key-press.ts +++ b/hooks/use-key-press.ts @@ -1,47 +1,47 @@ import { useEffect, useState } from 'react'; export function useKeyPress(targetKey: string, withMeta?: boolean): boolean { - // State for keeping track of whether key is pressed const [keyPressed, setKeyPressed] = useState(false); - // If pressed key is our target key then set to true - function downHandler({ - key, - metaKey, - }: { - key: string; - metaKey?: boolean; - }): void { - if (withMeta) { - if (key === targetKey && metaKey) { + + useEffect(() => { + function downHandler({ + key, + metaKey, + }: { + key: string; + metaKey?: boolean; + }): void { + if (withMeta) { + if (key === targetKey && metaKey) { + setKeyPressed(true); + } + return; + } + + if (key === targetKey) { setKeyPressed(true); } - return; } - if (key === targetKey) { - setKeyPressed(true); - } - } - // If released key is our target key then set to false - const upHandler = ({ - key, - }: { - key: string; - metaKey?: boolean; - }): void => { - if (key === targetKey) { - setKeyPressed(false); + function upHandler({ + key, + }: { + key: string; + metaKey?: boolean; + }): void { + if (key === targetKey) { + setKeyPressed(false); + } } - }; - // Add event listeners - useEffect(() => { + window.addEventListener('keydown', downHandler); window.addEventListener('keyup', upHandler); - // Remove event listeners on cleanup + return () => { window.removeEventListener('keydown', downHandler); window.removeEventListener('keyup', upHandler); }; - }, []); // Empty array ensures that effect is only run on mount and unmount + }, [targetKey, withMeta]); + return keyPressed; }