mirror of
https://github.com/twentyhq/twenty
synced 2026-04-21 13:37:22 +00:00
Improve type safety and remove unnecessary store operations (#18622)
## Summary This PR improves type safety across the codebase by replacing generic `any` types with proper TypeScript types, removes unnecessary record store operations, and adds TODO comments for future refactoring of useEffect hooks. ## Key Changes ### Type Safety Improvements - **SettingsAgentTurnDetail.tsx**: Replaced `any` type annotations with proper `AgentMessage` type from generated GraphQL types - **useCreateManyRecords.ts**: Added `RecordGqlNode` type for better type safety when handling mutation responses - **useLazyFindOneRecord.ts**: Replaced generic `Record<string, any>` with `Record<string, RecordGqlNode>` for improved type checking ### Removed Unnecessary Operations - **EventCardCalendarEvent.tsx**: Removed unused `useUpsertRecordsInStore` hook and its associated useEffect that was upserting calendar event records to the store - **EventCardMessage.tsx**: Removed unused `useUpsertRecordsInStore` hook and its associated useEffect that was upserting message records to the store ### Conditional Query Execution - **useLoadCurrentUser.ts**: Made the `FindAllCoreViewsDocument` query conditional - only executes when `isOnAWorkspace` is true, preventing unnecessary queries for users not on a workspace ### Documentation - Added TODO comments in multiple files (`useAgentChatData.ts`, `useWorkspaceFromInviteHash.ts`, `useGetPublicWorkspaceDataByDomain.ts`, `useFindManyRecords.ts`, `useSingleRecordPickerPerformSearch.ts`) referencing PR #18584 for future refactoring of useEffect hooks to avoid unnecessary re-renders ## Implementation Details - The removal of store upsert operations suggests these records are already being managed elsewhere or the operations were redundant - Type improvements maintain backward compatibility while providing better IDE support and compile-time checking - Conditional query execution reduces unnecessary network requests and improves performance for non-workspace users https://claude.ai/code/session_01YQErkoHotMvM6VL3JkWAqV --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
b470cb21a1
commit
4b6c8d52e5
11 changed files with 37 additions and 35 deletions
|
|
@ -8,9 +8,8 @@ import { type CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
|||
import { useOpenCalendarEventInSidePanel } from '@/side-panel/hooks/useOpenCalendarEventInSidePanel';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { UserContext } from '@/users/contexts/UserContext';
|
||||
import { useContext, useEffect } from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED } from 'twenty-shared/constants';
|
||||
import { CombinedGraphQLErrors } from '@apollo/client/errors';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
|
@ -97,7 +96,6 @@ export const EventCardCalendarEvent = ({
|
|||
}: {
|
||||
calendarEventId: string;
|
||||
}) => {
|
||||
const { upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
const { openCalendarEventInSidePanel } = useOpenCalendarEventInSidePanel();
|
||||
|
||||
const {
|
||||
|
|
@ -121,12 +119,6 @@ export const EventCardCalendarEvent = ({
|
|||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (calendarEvent) {
|
||||
upsertRecordsInStore({ partialRecords: [calendarEvent] });
|
||||
}
|
||||
}, [calendarEvent, upsertRecordsInStore]);
|
||||
|
||||
const { timeZone } = useContext(UserContext);
|
||||
|
||||
if (isDefined(error)) {
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@ import { EventCardMessageForbidden } from '@/activities/timeline-activities/rows
|
|||
import { useOpenEmailThreadInSidePanel } from '@/side-panel/hooks/useOpenEmailThreadInSidePanel';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import { useEffect } from 'react';
|
||||
import { FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED } from 'twenty-shared/constants';
|
||||
import { CombinedGraphQLErrors } from '@apollo/client/errors';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
|
@ -63,7 +61,6 @@ export const EventCardMessage = ({
|
|||
authorFullName: string;
|
||||
}) => {
|
||||
const { t } = useLingui();
|
||||
const { upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
const { openEmailThreadInSidePanel } = useOpenEmailThreadInSidePanel();
|
||||
|
||||
const {
|
||||
|
|
@ -85,12 +82,6 @@ export const EventCardMessage = ({
|
|||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (message) {
|
||||
upsertRecordsInStore({ partialRecords: [message] });
|
||||
}
|
||||
}, [message, upsertRecordsInStore]);
|
||||
|
||||
if (isDefined(error)) {
|
||||
if (CombinedGraphQLErrors.is(error)) {
|
||||
const shouldHideMessageContent = error.errors.some(
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ export const useAgentChatData = () => {
|
|||
},
|
||||
);
|
||||
|
||||
// TODO: Refactor this useEffect to avoid unnecessary re-renders (see PR #18584 review)
|
||||
useEffect(() => {
|
||||
if (!threadsData) return;
|
||||
|
||||
|
|
@ -204,6 +205,7 @@ export const useAgentChatData = () => {
|
|||
skip: !isDefined(currentAIChatThread) || isNewThread,
|
||||
});
|
||||
|
||||
// TODO: Refactor this useEffect to avoid unnecessary re-renders (see PR #18584 review)
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
store.set(skipMessagesSkeletonUntilLoadedState.atom, false);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ export const useWorkspaceFromInviteHash = () => {
|
|||
}
|
||||
}, [error, enqueueErrorSnackBar, navigate]);
|
||||
|
||||
// TODO: Rework this useEffect - Charles will refactor as part of auth rework
|
||||
useEffect(() => {
|
||||
if (!workspaceFromInviteHash || hasRedirected) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ export const useGetPublicWorkspaceDataByDomain = () => {
|
|||
},
|
||||
);
|
||||
|
||||
// TODO: Refactor these useEffects to avoid unnecessary re-renders (see PR #18584 review)
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setWorkspaceAuthProviders(
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useU
|
|||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { computeOptimisticRecordFromInput } from '@/object-record/utils/computeOptimisticRecordFromInput';
|
||||
import { dispatchObjectRecordOperationBrowserEvent } from '@/browser-event/utils/dispatchObjectRecordOperationBrowserEvent';
|
||||
import { type RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||
import { getCreateManyRecordsMutationResponseField } from '@/object-record/utils/getCreateManyRecordsMutationResponseField';
|
||||
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
|
|
@ -195,7 +196,7 @@ export const useCreateManyRecords = <
|
|||
},
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const records = (data as Record<string, any>)?.[
|
||||
const records = (data as Record<string, RecordGqlNode[]> | null)?.[
|
||||
mutationResponseField
|
||||
];
|
||||
|
||||
|
|
@ -254,8 +255,9 @@ export const useCreateManyRecords = <
|
|||
});
|
||||
|
||||
return (
|
||||
(createdObjects.data as Record<string, any>)?.[mutationResponseField] ??
|
||||
[]
|
||||
(createdObjects.data as Record<string, RecordGqlNode[]> | null)?.[
|
||||
mutationResponseField
|
||||
] ?? []
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
|
|||
client: apolloCoreClient,
|
||||
});
|
||||
|
||||
// TODO: Refactor these useEffects to avoid unnecessary re-renders (see PR #18584 review)
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
handleFindManyRecordsCompleted(data);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient
|
|||
import { type ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
||||
import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode';
|
||||
import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject';
|
||||
import { type RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||
import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types';
|
||||
import { useFindOneRecordQuery } from '@/object-record/hooks/useFindOneRecordQuery';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
|
|
@ -58,7 +59,9 @@ export const useLazyFindOneRecord = <T extends ObjectRecord = ObjectRecord>({
|
|||
}).retain();
|
||||
if (result.data) {
|
||||
const record = getRecordFromRecordNode<T>({
|
||||
recordNode: (result.data as Record<string, any>)[objectNameSingular],
|
||||
recordNode: (result.data as Record<string, RecordGqlNode>)[
|
||||
objectNameSingular
|
||||
],
|
||||
});
|
||||
onCompleted?.(record);
|
||||
}
|
||||
|
|
@ -66,6 +69,9 @@ export const useLazyFindOneRecord = <T extends ObjectRecord = ObjectRecord>({
|
|||
called,
|
||||
error,
|
||||
loading,
|
||||
record: (data as Record<string, any>)?.[objectNameSingular] || undefined,
|
||||
record:
|
||||
(data as Record<string, RecordGqlNode> | undefined)?.[
|
||||
objectNameSingular
|
||||
] || undefined,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ export const useSingleRecordPickerPerformSearch = ({
|
|||
[selectedRecords, filteredSelectedRecords, recordsToSelect],
|
||||
);
|
||||
|
||||
// TODO: Refactor this useEffect to avoid unnecessary re-renders (see PR #18584 review)
|
||||
useEffect(() => {
|
||||
allSearchRecords.forEach((searchRecord) => {
|
||||
store.set(
|
||||
|
|
|
|||
|
|
@ -55,10 +55,12 @@ export const useLoadCurrentUser = () => {
|
|||
fetchPolicy: 'network-only',
|
||||
});
|
||||
|
||||
const coreViewsResult = await client.query({
|
||||
query: FindAllCoreViewsDocument,
|
||||
fetchPolicy: 'network-only',
|
||||
});
|
||||
const coreViewsResult = isOnAWorkspace
|
||||
? await client.query({
|
||||
query: FindAllCoreViewsDocument,
|
||||
fetchPolicy: 'network-only',
|
||||
})
|
||||
: undefined;
|
||||
|
||||
if (isDefined(currentUserResult.error)) {
|
||||
throw new Error(currentUserResult.error.message);
|
||||
|
|
@ -137,7 +139,7 @@ export const useLoadCurrentUser = () => {
|
|||
});
|
||||
}
|
||||
|
||||
if (isDefined(coreViewsResult.data?.getCoreViews)) {
|
||||
if (isDefined(coreViewsResult?.data?.getCoreViews)) {
|
||||
setCoreViews(coreViewsResult.data.getCoreViews);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@ import { getSettingsPath } from 'twenty-shared/utils';
|
|||
import { H2Title, Status } from 'twenty-ui/display';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { GetAgentTurnsDocument } from '~/generated-metadata/graphql';
|
||||
import {
|
||||
type AgentMessage,
|
||||
GetAgentTurnsDocument,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledTableContainer = styled.div`
|
||||
margin-top: ${themeCssVariables.spacing[3]};
|
||||
|
|
@ -61,7 +64,7 @@ export const SettingsAgentTurnDetail = () => {
|
|||
skip: !agentId,
|
||||
});
|
||||
|
||||
const turn = data?.agentTurns?.find((t: any) => t.id === turnId);
|
||||
const turn = data?.agentTurns?.find((t) => t.id === turnId);
|
||||
|
||||
const getScoreColor = (score: number) => {
|
||||
if (score >= 80) return 'green';
|
||||
|
|
@ -147,9 +150,9 @@ export const SettingsAgentTurnDetail = () => {
|
|||
{turn.messages.length > 0 ? (
|
||||
<StyledMessagesContainer>
|
||||
{mapDBMessagesToUIMessages(
|
||||
([...turn.messages] as any[])
|
||||
.filter((msg: any) => msg.parts && msg.parts.length > 0)
|
||||
.sort((a: any, b: any) => {
|
||||
([...turn.messages] as AgentMessage[])
|
||||
.filter((msg) => msg.parts.length > 0)
|
||||
.sort((a, b) => {
|
||||
if (a.role === 'user' && b.role === 'assistant') return -1;
|
||||
if (a.role === 'assistant' && b.role === 'user') return 1;
|
||||
return (
|
||||
|
|
@ -196,11 +199,11 @@ export const SettingsAgentTurnDetail = () => {
|
|||
</StyledTableHeaderRowContainer>
|
||||
{[...turn.evaluations]
|
||||
.sort(
|
||||
(a: any, b: any) =>
|
||||
(a, b) =>
|
||||
new Date(b.createdAt).getTime() -
|
||||
new Date(a.createdAt).getTime(),
|
||||
)
|
||||
.map((evaluation: any) => (
|
||||
.map((evaluation) => (
|
||||
<TableRow
|
||||
key={evaluation.id}
|
||||
gridTemplateColumns="140px 80px 1fr"
|
||||
|
|
|
|||
Loading…
Reference in a new issue