[WIP] Whole FE migrated (#2517)
* Wip * WIP * Removed concole log * Add relations to workspace init (#2511) * Add relations to workspace init * remove logs * update prefill * add missing isSystem * comment relation fields * Migrate v2 core models to graphql schema (#2509) * migrate v2 core models to graphql schema * Migrate to new workspace member schema * Continue work * migrated-main * Finished accountOwner nested field integration on companies * Introduce bug * Fix --------- Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com> Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
@ -3,6 +3,7 @@ import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { GET_COMPANIES } from '@/companies/graphql/queries/getCompanies';
|
||||
import { GET_PEOPLE } from '@/people/graphql/queries/getPeople';
|
||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||
@ -23,6 +24,7 @@ export const useOpenCreateActivityDrawer = () => {
|
||||
const { openRightDrawer } = useRightDrawer();
|
||||
const [createActivityMutation] = useCreateActivityMutation();
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
|
||||
const [, setActivityTargetableEntityArray] = useRecoilState(
|
||||
@ -49,11 +51,11 @@ export const useOpenCreateActivityDrawer = () => {
|
||||
updatedAt: now,
|
||||
author: { connect: { id: currentUser?.id ?? '' } },
|
||||
workspaceMemberAuthor: {
|
||||
connect: { id: currentUser?.workspaceMember?.id ?? '' },
|
||||
connect: { id: currentWorkspaceMember?.id ?? '' },
|
||||
},
|
||||
assignee: { connect: { id: assigneeId ?? currentUser?.id ?? '' } },
|
||||
workspaceMemberAssignee: {
|
||||
connect: { id: currentUser?.workspaceMember?.id ?? '' },
|
||||
connect: { id: currentWorkspaceMember?.id ?? '' },
|
||||
},
|
||||
type: type,
|
||||
activityTargets: {
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { DateTime } from 'luxon';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { turnFilterIntoWhereClause } from '@/ui/object/object-filter-dropdown/utils/turnFilterIntoWhereClause';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { ActivityType, useGetActivitiesQuery } from '~/generated/graphql';
|
||||
@ -9,6 +10,7 @@ import { parseDate } from '~/utils/date-utils';
|
||||
|
||||
export const useCurrentUserTaskCount = () => {
|
||||
const [currentUser] = useRecoilState(currentUserState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
|
||||
const { data } = useGetActivitiesQuery({
|
||||
variables: {
|
||||
@ -20,8 +22,11 @@ export const useCurrentUserTaskCount = () => {
|
||||
fieldMetadataId: 'assigneeId',
|
||||
value: currentUser.id,
|
||||
operand: ViewFilterOperand.Is,
|
||||
displayValue: currentUser.displayName,
|
||||
displayAvatarUrl: currentUser.avatarUrl ?? undefined,
|
||||
displayValue:
|
||||
currentWorkspaceMember?.firstName +
|
||||
' ' +
|
||||
currentWorkspaceMember?.lastName,
|
||||
displayAvatarUrl: currentWorkspaceMember?.avatarUrl ?? undefined,
|
||||
definition: {
|
||||
type: 'ENTITY',
|
||||
},
|
||||
|
||||
@ -9,7 +9,7 @@ import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { GET_COMPANIES } from '@/companies/graphql/queries/getCompanies';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { generateFindManyCustomObjectsQuery } from '@/object-record/utils/generateFindManyCustomObjectsQuery';
|
||||
import { useGenerateFindManyCustomObjectsQuery } from '@/object-record/utils/useGenerateFindManyCustomObjectsQuery';
|
||||
import { GET_PEOPLE } from '@/people/graphql/queries/getPeople';
|
||||
import { GET_API_KEYS } from '@/settings/developers/graphql/queries/getApiKeys';
|
||||
import {
|
||||
@ -54,7 +54,7 @@ export const useOptimisticEffect = () => {
|
||||
objectMetadataItem?: ObjectMetadataItem;
|
||||
}) => {
|
||||
if (isUsingFlexibleBackend && objectMetadataItem) {
|
||||
const generatedQuery = generateFindManyCustomObjectsQuery({
|
||||
const generatedQuery = useGenerateFindManyCustomObjectsQuery({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
|
||||
@ -9,49 +9,5 @@ export const USER_QUERY_FRAGMENT = gql`
|
||||
lastName
|
||||
canImpersonate
|
||||
supportUserHash
|
||||
avatarUrl
|
||||
workspaceMember {
|
||||
id
|
||||
allowImpersonation
|
||||
workspace {
|
||||
id
|
||||
domainName
|
||||
displayName
|
||||
logo
|
||||
inviteHash
|
||||
}
|
||||
assignedActivities {
|
||||
id
|
||||
title
|
||||
}
|
||||
authoredActivities {
|
||||
id
|
||||
title
|
||||
}
|
||||
authoredAttachments {
|
||||
id
|
||||
name
|
||||
type
|
||||
}
|
||||
settings {
|
||||
id
|
||||
colorScheme
|
||||
locale
|
||||
}
|
||||
companies {
|
||||
id
|
||||
name
|
||||
domainName
|
||||
}
|
||||
comments {
|
||||
id
|
||||
body
|
||||
}
|
||||
}
|
||||
settings {
|
||||
id
|
||||
colorScheme
|
||||
locale
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@ -4,12 +4,19 @@ import {
|
||||
snapshot_UNSTABLE,
|
||||
useGotoRecoilSnapshot,
|
||||
useRecoilState,
|
||||
useSetRecoilState,
|
||||
} from 'recoil';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState';
|
||||
import { CREATE_ONE_WORKSPACE_MEMBER_V2 } from '@/object-record/graphql/mutation/createOneWorkspaceMember';
|
||||
import { FIND_ONE_WORKSPACE_MEMBER_V2 } from '@/object-record/graphql/queries/findOneWorkspaceMember';
|
||||
import { REACT_APP_SERVER_AUTH_URL } from '~/config';
|
||||
import {
|
||||
useChallengeMutation,
|
||||
useCheckUserExistsLazyQuery,
|
||||
useGetCurrentWorkspaceLazyQuery,
|
||||
useSignUpMutation,
|
||||
useVerifyMutation,
|
||||
} from '~/generated/graphql';
|
||||
@ -19,13 +26,20 @@ import { tokenPairState } from '../states/tokenPairState';
|
||||
|
||||
export const useAuth = () => {
|
||||
const [, setTokenPair] = useRecoilState(tokenPairState);
|
||||
const [, setCurrentUser] = useRecoilState(currentUserState);
|
||||
const setCurrentUser = useSetRecoilState(currentUserState);
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
const setIsVerifyPendingState = useSetRecoilState(isVerifyPendingState);
|
||||
|
||||
const [challenge] = useChallengeMutation();
|
||||
const [signUp] = useSignUpMutation();
|
||||
const [verify] = useVerifyMutation();
|
||||
const [checkUserExistsQuery, { data: checkUserExistsData }] =
|
||||
useCheckUserExistsLazyQuery();
|
||||
const [getCurrentWorkspaceQuery, { data: getCurrentWorkspaceData }] =
|
||||
useGetCurrentWorkspaceLazyQuery();
|
||||
|
||||
const client = useApolloClient();
|
||||
|
||||
@ -67,36 +81,56 @@ export const useAuth = () => {
|
||||
throw new Error('No verify result');
|
||||
}
|
||||
|
||||
if (!verifyResult.data?.verify.user.workspaceMember) {
|
||||
throw new Error('No workspace member');
|
||||
}
|
||||
|
||||
if (!verifyResult.data?.verify.user.workspaceMember.settings) {
|
||||
throw new Error('No settings');
|
||||
}
|
||||
|
||||
setCurrentUser({
|
||||
...verifyResult.data?.verify.user,
|
||||
workspaceMember: {
|
||||
...verifyResult.data?.verify.user.workspaceMember,
|
||||
settings: verifyResult.data?.verify.user.workspaceMember.settings,
|
||||
setTokenPair(verifyResult.data?.verify.tokens);
|
||||
const workspaceMember = await client.query({
|
||||
query: FIND_ONE_WORKSPACE_MEMBER_V2,
|
||||
variables: {
|
||||
filter: {
|
||||
userId: { eq: verifyResult.data?.verify.user.id },
|
||||
},
|
||||
},
|
||||
});
|
||||
setTokenPair(verifyResult.data?.verify.tokens);
|
||||
const currentWorkspace = await getCurrentWorkspaceQuery();
|
||||
|
||||
return verifyResult.data?.verify;
|
||||
setCurrentUser(verifyResult.data?.verify.user);
|
||||
setCurrentWorkspaceMember(workspaceMember.data?.findMany);
|
||||
setCurrentWorkspace(currentWorkspace.data?.currentWorkspace ?? null);
|
||||
return {
|
||||
user: verifyResult.data?.verify.user,
|
||||
workspaceMember: workspaceMember.data?.findMany,
|
||||
workspace: currentWorkspace.data?.currentWorkspace,
|
||||
tokens: verifyResult.data?.verify.tokens,
|
||||
};
|
||||
},
|
||||
[setTokenPair, verify, setCurrentUser],
|
||||
[
|
||||
verify,
|
||||
setTokenPair,
|
||||
client,
|
||||
getCurrentWorkspaceQuery,
|
||||
setCurrentUser,
|
||||
setCurrentWorkspaceMember,
|
||||
setCurrentWorkspace,
|
||||
],
|
||||
);
|
||||
|
||||
const handleCrendentialsSignIn = useCallback(
|
||||
async (email: string, password: string) => {
|
||||
const { loginToken } = await handleChallenge(email, password);
|
||||
setIsVerifyPendingState(true);
|
||||
|
||||
const { user } = await handleVerify(loginToken.token);
|
||||
return user;
|
||||
const { user, workspaceMember, workspace } = await handleVerify(
|
||||
loginToken.token,
|
||||
);
|
||||
|
||||
setIsVerifyPendingState(false);
|
||||
|
||||
return {
|
||||
user,
|
||||
workspaceMember,
|
||||
workspace,
|
||||
};
|
||||
},
|
||||
[handleChallenge, handleVerify],
|
||||
[handleChallenge, handleVerify, setIsVerifyPendingState],
|
||||
);
|
||||
|
||||
const handleSignOut = useCallback(() => {
|
||||
@ -110,6 +144,8 @@ export const useAuth = () => {
|
||||
|
||||
const handleCredentialsSignUp = useCallback(
|
||||
async (email: string, password: string, workspaceInviteHash?: string) => {
|
||||
setIsVerifyPendingState(true);
|
||||
|
||||
const signUpResult = await signUp({
|
||||
variables: {
|
||||
email,
|
||||
@ -126,13 +162,30 @@ export const useAuth = () => {
|
||||
throw new Error('No login token');
|
||||
}
|
||||
|
||||
const { user } = await handleVerify(
|
||||
const { user, workspace } = await handleVerify(
|
||||
signUpResult.data?.signUp.loginToken.token,
|
||||
);
|
||||
|
||||
return user;
|
||||
const workspaceMember = await client.mutate({
|
||||
mutation: CREATE_ONE_WORKSPACE_MEMBER_V2,
|
||||
variables: {
|
||||
input: {
|
||||
firstName: user.firstName ?? '',
|
||||
lastName: user.lastName ?? '',
|
||||
colorScheme: 'Light',
|
||||
userId: user.id,
|
||||
allowImpersonation: true,
|
||||
locale: 'en',
|
||||
},
|
||||
},
|
||||
});
|
||||
setCurrentWorkspaceMember(workspaceMember.data?.createWorkspaceMemberV2);
|
||||
|
||||
setIsVerifyPendingState(false);
|
||||
|
||||
return { user, workspaceMember, workspace };
|
||||
},
|
||||
[signUp, handleVerify],
|
||||
[setIsVerifyPendingState, signUp, handleVerify, client],
|
||||
);
|
||||
|
||||
const handleGoogleLogin = useCallback((workspaceInviteHash?: string) => {
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState';
|
||||
|
||||
import { tokenPairState } from '../states/tokenPairState';
|
||||
|
||||
export const useIsLogged = (): boolean => {
|
||||
const [tokenPair] = useRecoilState(tokenPairState);
|
||||
const isVerifyPending = useRecoilValue(isVerifyPendingState);
|
||||
|
||||
return !!tokenPair;
|
||||
return !!tokenPair && !isVerifyPending;
|
||||
};
|
||||
|
||||
@ -1,15 +1,26 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
|
||||
import { useIsLogged } from '../hooks/useIsLogged';
|
||||
import { currentUserState } from '../states/currentUserState';
|
||||
import {
|
||||
getOnboardingStatus,
|
||||
OnboardingStatus,
|
||||
} from '../utils/getOnboardingStatus';
|
||||
|
||||
export const useOnboardingStatus = (): OnboardingStatus | undefined => {
|
||||
const [currentUser] = useRecoilState(currentUserState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const isLoggedIn = useIsLogged();
|
||||
|
||||
return getOnboardingStatus(isLoggedIn, currentUser);
|
||||
console.log(
|
||||
getOnboardingStatus(isLoggedIn, currentWorkspaceMember, currentWorkspace),
|
||||
);
|
||||
|
||||
return getOnboardingStatus(
|
||||
isLoggedIn,
|
||||
currentWorkspaceMember,
|
||||
currentWorkspace,
|
||||
);
|
||||
};
|
||||
|
||||
@ -62,7 +62,7 @@ export const useSignInUp = () => {
|
||||
});
|
||||
const [showErrors, setShowErrors] = useState(false);
|
||||
|
||||
const { data: workspace } = useGetWorkspaceFromInviteHashQuery({
|
||||
const { data: workspaceFromInviteHash } = useGetWorkspaceFromInviteHashQuery({
|
||||
variables: { inviteHash: workspaceInviteHash || '' },
|
||||
});
|
||||
|
||||
@ -119,20 +119,23 @@ export const useSignInUp = () => {
|
||||
if (!data.email || !data.password) {
|
||||
throw new Error('Email and password are required');
|
||||
}
|
||||
let user;
|
||||
let currentWorkspace;
|
||||
|
||||
if (signInUpMode === SignInUpMode.SignIn) {
|
||||
user = await signInWithCredentials(
|
||||
const { workspace } = await signInWithCredentials(
|
||||
data.email.toLowerCase(),
|
||||
data.password,
|
||||
);
|
||||
currentWorkspace = workspace;
|
||||
} else {
|
||||
user = await signUpWithCredentials(
|
||||
const { workspace } = await signUpWithCredentials(
|
||||
data.email.toLowerCase(),
|
||||
data.password,
|
||||
workspaceInviteHash,
|
||||
);
|
||||
currentWorkspace = workspace;
|
||||
}
|
||||
if (user?.workspaceMember?.workspace?.displayName) {
|
||||
if (currentWorkspace?.displayName) {
|
||||
navigate('/');
|
||||
} else {
|
||||
navigate('/create/workspace');
|
||||
@ -144,12 +147,12 @@ export const useSignInUp = () => {
|
||||
}
|
||||
},
|
||||
[
|
||||
navigate,
|
||||
signInUpMode,
|
||||
signInWithCredentials,
|
||||
signUpWithCredentials,
|
||||
workspaceInviteHash,
|
||||
navigate,
|
||||
enqueueSnackBar,
|
||||
signInUpMode,
|
||||
],
|
||||
);
|
||||
|
||||
@ -188,6 +191,6 @@ export const useSignInUp = () => {
|
||||
goBackToEmailStep,
|
||||
submitCredentials,
|
||||
form,
|
||||
workspace: workspace?.findWorkspaceFromInviteHash,
|
||||
workspace: workspaceFromInviteHash?.findWorkspaceFromInviteHash,
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,32 +1,11 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
import {
|
||||
User,
|
||||
UserSettings,
|
||||
Workspace,
|
||||
WorkspaceMember,
|
||||
} from '~/generated/graphql';
|
||||
import { User } from '~/generated/graphql';
|
||||
|
||||
export type CurrentUser = Pick<
|
||||
User,
|
||||
| 'id'
|
||||
| 'email'
|
||||
| 'displayName'
|
||||
| 'firstName'
|
||||
| 'lastName'
|
||||
| 'avatarUrl'
|
||||
| 'canImpersonate'
|
||||
| 'supportUserHash'
|
||||
> & {
|
||||
workspaceMember: Pick<WorkspaceMember, 'id' | 'allowImpersonation'> & {
|
||||
workspace: Pick<
|
||||
Workspace,
|
||||
'id' | 'displayName' | 'domainName' | 'inviteHash' | 'logo'
|
||||
>;
|
||||
settings: Pick<UserSettings, 'id' | 'colorScheme' | 'locale'>;
|
||||
};
|
||||
settings: Pick<UserSettings, 'id' | 'colorScheme' | 'locale'>;
|
||||
};
|
||||
'id' | 'email' | 'supportUserHash' | 'canImpersonate'
|
||||
>;
|
||||
|
||||
export const currentUserState = atom<CurrentUser | null>({
|
||||
key: 'currentUserState',
|
||||
|
||||
18
front/src/modules/auth/states/currentWorkspaceMemberState.ts
Normal file
18
front/src/modules/auth/states/currentWorkspaceMemberState.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
import { ColorScheme } from '~/generated-metadata/graphql';
|
||||
|
||||
export type CurrentWorkspaceMember = {
|
||||
id: string;
|
||||
locale: string;
|
||||
colorScheme: ColorScheme;
|
||||
allowImpersonation: boolean;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
avatarUrl: string;
|
||||
};
|
||||
|
||||
export const currentWorkspaceMemberState = atom<CurrentWorkspaceMember | null>({
|
||||
key: 'currentWorkspaceMemberState',
|
||||
default: null,
|
||||
});
|
||||
13
front/src/modules/auth/states/currentWorkspaceState.ts
Normal file
13
front/src/modules/auth/states/currentWorkspaceState.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
import { Workspace } from '~/generated-metadata/graphql';
|
||||
|
||||
export type CurrentWorkspace = Pick<
|
||||
Workspace,
|
||||
'id' | 'inviteHash' | 'logo' | 'displayName'
|
||||
>;
|
||||
|
||||
export const currentWorkspaceState = atom<CurrentWorkspace | null>({
|
||||
key: 'currentWorkspaceState',
|
||||
default: null,
|
||||
});
|
||||
6
front/src/modules/auth/states/isVerifyPendingState.ts
Normal file
6
front/src/modules/auth/states/isVerifyPendingState.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const isVerifyPendingState = atom<boolean>({
|
||||
key: 'isVerifyPendingState',
|
||||
default: false,
|
||||
});
|
||||
@ -1,4 +1,5 @@
|
||||
import { CurrentUser } from '../states/currentUserState';
|
||||
import { CurrentWorkspaceMember } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { CurrentWorkspace } from '@/auth/states/currentWorkspaceState';
|
||||
|
||||
export enum OnboardingStatus {
|
||||
OngoingUserCreation = 'ongoing_user_creation',
|
||||
@ -9,21 +10,22 @@ export enum OnboardingStatus {
|
||||
|
||||
export const getOnboardingStatus = (
|
||||
isLoggedIn: boolean,
|
||||
currentUser: CurrentUser | null,
|
||||
currentWorkspaceMember: CurrentWorkspaceMember | null,
|
||||
currentWorkspace: CurrentWorkspace | null,
|
||||
) => {
|
||||
if (!isLoggedIn) {
|
||||
return OnboardingStatus.OngoingUserCreation;
|
||||
}
|
||||
|
||||
// if the user has not been fetched yet, we can't know the onboarding status
|
||||
if (!currentUser) {
|
||||
if (!currentWorkspaceMember) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!currentUser.workspaceMember?.workspace.displayName) {
|
||||
if (!currentWorkspace?.displayName) {
|
||||
return OnboardingStatus.OngoingWorkspaceCreation;
|
||||
}
|
||||
if (!currentUser.firstName || !currentUser.lastName) {
|
||||
if (!currentWorkspaceMember.firstName || !currentWorkspaceMember.lastName) {
|
||||
return OnboardingStatus.OngoingProfileCreation;
|
||||
}
|
||||
|
||||
|
||||
@ -27,7 +27,6 @@ export const FIND_MANY_METADATA_OBJECTS = gql`
|
||||
label
|
||||
description
|
||||
icon
|
||||
placeholder
|
||||
isCustom
|
||||
isActive
|
||||
isNullable
|
||||
@ -36,10 +35,34 @@ export const FIND_MANY_METADATA_OBJECTS = gql`
|
||||
fromRelationMetadata {
|
||||
id
|
||||
relationType
|
||||
toObjectMetadata {
|
||||
id
|
||||
dataSourceId
|
||||
nameSingular
|
||||
namePlural
|
||||
}
|
||||
fromObjectMetadata {
|
||||
id
|
||||
dataSourceId
|
||||
nameSingular
|
||||
namePlural
|
||||
}
|
||||
}
|
||||
toRelationMetadata {
|
||||
id
|
||||
relationType
|
||||
toObjectMetadata {
|
||||
id
|
||||
dataSourceId
|
||||
nameSingular
|
||||
namePlural
|
||||
}
|
||||
fromObjectMetadata {
|
||||
id
|
||||
dataSourceId
|
||||
nameSingular
|
||||
namePlural
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import {
|
||||
import { logError } from '~/utils/logError';
|
||||
|
||||
import { FIND_MANY_METADATA_OBJECTS } from '../graphql/queries';
|
||||
import { formatPagedObjectMetadataItemsToObjectMetadataItems } from '../utils/formatPagedObjectMetadataItemsToObjectMetadataItems';
|
||||
import { mapPaginatedObjectMetadataItemsToObjectMetadataItems } from '../utils/mapPaginatedObjectMetadataItemsToObjectMetadataItems';
|
||||
|
||||
import { useApolloMetadataClient } from './useApolloMetadataClient';
|
||||
|
||||
@ -59,7 +59,7 @@ export const useFindManyObjectMetadataItems = ({
|
||||
});
|
||||
|
||||
const objectMetadataItems = useMemo(() => {
|
||||
return formatPagedObjectMetadataItemsToObjectMetadataItems({
|
||||
return mapPaginatedObjectMetadataItemsToObjectMetadataItems({
|
||||
pagedObjectMetadataItems: data,
|
||||
});
|
||||
}, [data]);
|
||||
|
||||
@ -0,0 +1,74 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar/hooks/useSnackBar';
|
||||
import {
|
||||
ObjectFilter,
|
||||
ObjectMetadataItemsQuery,
|
||||
ObjectMetadataItemsQueryVariables,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { logError } from '~/utils/logError';
|
||||
|
||||
import { FIND_MANY_METADATA_OBJECTS } from '../graphql/queries';
|
||||
import { mapPaginatedObjectMetadataItemsToObjectMetadataItems } from '../utils/mapPaginatedObjectMetadataItemsToObjectMetadataItems';
|
||||
|
||||
import { useApolloMetadataClient } from './useApolloMetadataClient';
|
||||
|
||||
// TODO: test fetchMore
|
||||
export const useFindManyObjectMetadataItems = ({
|
||||
skip,
|
||||
filter,
|
||||
}: { skip?: boolean; filter?: ObjectFilter } = {}) => {
|
||||
const apolloMetadataClient = useApolloMetadataClient();
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
|
||||
const {
|
||||
data,
|
||||
fetchMore: fetchMoreInternal,
|
||||
loading,
|
||||
error,
|
||||
} = useQuery<ObjectMetadataItemsQuery, ObjectMetadataItemsQueryVariables>(
|
||||
FIND_MANY_METADATA_OBJECTS,
|
||||
{
|
||||
variables: {
|
||||
filter,
|
||||
},
|
||||
client: apolloMetadataClient ?? undefined,
|
||||
skip: skip || !apolloMetadataClient,
|
||||
onError: (error) => {
|
||||
logError('useFindManyObjectMetadataItems error : ' + error);
|
||||
enqueueSnackBar(
|
||||
`Error during useFindManyObjectMetadataItems, ${error.message}`,
|
||||
{
|
||||
variant: 'error',
|
||||
},
|
||||
);
|
||||
},
|
||||
onCompleted: () => {},
|
||||
},
|
||||
);
|
||||
|
||||
const hasMore = data?.objects?.pageInfo?.hasNextPage;
|
||||
|
||||
const fetchMore = () =>
|
||||
fetchMoreInternal({
|
||||
variables: {
|
||||
afterCursor: data?.objects?.pageInfo?.endCursor,
|
||||
},
|
||||
});
|
||||
|
||||
const objectMetadataItems = useMemo(() => {
|
||||
return mapPaginatedObjectMetadataItemsToObjectMetadataItems({
|
||||
pagedObjectMetadataItems: data,
|
||||
});
|
||||
}, [data]);
|
||||
|
||||
return {
|
||||
objectMetadataItems,
|
||||
hasMore,
|
||||
fetchMore,
|
||||
loading,
|
||||
error,
|
||||
};
|
||||
};
|
||||
@ -1,10 +1,10 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { generateCreateOneObjectMutation } from '@/object-record/utils/generateCreateOneObjectMutation';
|
||||
import { generateDeleteOneObjectMutation } from '@/object-record/utils/generateDeleteOneObjectMutation';
|
||||
import { generateFindManyCustomObjectsQuery } from '@/object-record/utils/generateFindManyCustomObjectsQuery';
|
||||
import { generateFindOneCustomObjectQuery } from '@/object-record/utils/generateFindOneCustomObjectQuery';
|
||||
import { generateUpdateOneObjectMutation } from '@/object-record/utils/generateUpdateOneObjectMutation';
|
||||
import { useGenerateCreateOneObjectMutation } from '@/object-record/utils/generateCreateOneObjectMutation';
|
||||
import { useGenerateDeleteOneObjectMutation } from '@/object-record/utils/useGenerateDeleteOneObjectMutation';
|
||||
import { useGenerateFindManyCustomObjectsQuery } from '@/object-record/utils/useGenerateFindManyCustomObjectsQuery';
|
||||
import { useGenerateFindOneCustomObjectQuery } from '@/object-record/utils/useGenerateFindOneCustomObjectQuery';
|
||||
import { useGenerateUpdateOneObjectMutation } from '@/object-record/utils/useGenerateUpdateOneObjectMutation';
|
||||
import { useLazyLoadIcons } from '@/ui/input/hooks/useLazyLoadIcons';
|
||||
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
|
||||
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
|
||||
@ -16,13 +16,13 @@ import { formatFieldMetadataItemsAsSortDefinitions } from '../utils/formatFieldM
|
||||
|
||||
import { useFindManyObjectMetadataItems } from './useFindManyObjectMetadataItems';
|
||||
|
||||
const EMPTY_QUERY = gql`
|
||||
export const EMPTY_QUERY = gql`
|
||||
query EmptyQuery {
|
||||
empty
|
||||
}
|
||||
`;
|
||||
|
||||
const EMPTY_MUTATION = gql`
|
||||
export const EMPTY_MUTATION = gql`
|
||||
mutation EmptyMutation {
|
||||
empty
|
||||
}
|
||||
@ -74,35 +74,25 @@ export const useFindOneObjectMetadataItem = ({
|
||||
icons,
|
||||
});
|
||||
|
||||
const findManyQuery = foundObjectMetadataItem
|
||||
? generateFindManyCustomObjectsQuery({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
})
|
||||
: EMPTY_QUERY;
|
||||
const findManyQuery = useGenerateFindManyCustomObjectsQuery({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
});
|
||||
|
||||
const findOneQuery = foundObjectMetadataItem
|
||||
? generateFindOneCustomObjectQuery({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
})
|
||||
: EMPTY_QUERY;
|
||||
const findOneQuery = useGenerateFindOneCustomObjectQuery({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
});
|
||||
|
||||
const createOneMutation = foundObjectMetadataItem
|
||||
? generateCreateOneObjectMutation({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
})
|
||||
: EMPTY_MUTATION;
|
||||
const createOneMutation = useGenerateCreateOneObjectMutation({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
});
|
||||
|
||||
const updateOneMutation = foundObjectMetadataItem
|
||||
? generateUpdateOneObjectMutation({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
})
|
||||
: EMPTY_MUTATION;
|
||||
const updateOneMutation = useGenerateUpdateOneObjectMutation({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
});
|
||||
|
||||
const deleteOneMutation = foundObjectMetadataItem
|
||||
? generateDeleteOneObjectMutation({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
})
|
||||
: EMPTY_MUTATION;
|
||||
const deleteOneMutation = useGenerateDeleteOneObjectMutation({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
});
|
||||
|
||||
return {
|
||||
foundObjectMetadataItem,
|
||||
|
||||
@ -0,0 +1,86 @@
|
||||
import { useFindManyObjectMetadataItems } from '@/object-metadata/hooks/useFindManyObjectMetadataItems';
|
||||
import { FieldType } from '@/ui/object/field/types/FieldType';
|
||||
|
||||
import { FieldMetadataItem } from '../types/FieldMetadataItem';
|
||||
|
||||
export const useMapFieldMetadataToGraphQLQuery = () => {
|
||||
const { objectMetadataItems } = useFindManyObjectMetadataItems();
|
||||
|
||||
const mapFieldMetadataToGraphQLQuery = (field: FieldMetadataItem): any => {
|
||||
// TODO: parse
|
||||
const fieldType = field.type as FieldType;
|
||||
|
||||
const fieldIsSimpleValue = (
|
||||
[
|
||||
'TEXT',
|
||||
'PHONE',
|
||||
'DATE',
|
||||
'EMAIL',
|
||||
'NUMBER',
|
||||
'BOOLEAN',
|
||||
'DATE',
|
||||
] as FieldType[]
|
||||
).includes(fieldType);
|
||||
|
||||
const fieldIsURL = fieldType === 'URL' || fieldType === 'URL_V2';
|
||||
|
||||
const fieldIsMoneyAmount =
|
||||
fieldType === 'MONEY' || fieldType === 'MONEY_AMOUNT_V2';
|
||||
|
||||
if (fieldIsSimpleValue) {
|
||||
return field.name;
|
||||
} else if (
|
||||
fieldType === 'RELATION' &&
|
||||
field.toRelationMetadata?.relationType === 'ONE_TO_MANY'
|
||||
) {
|
||||
console.log({ objectMetadataItems, field });
|
||||
|
||||
const relationMetadataItem = objectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.id ===
|
||||
(field.toRelationMetadata as any)?.fromObjectMetadata?.id,
|
||||
);
|
||||
|
||||
console.log({ relationMetadataItem });
|
||||
|
||||
return `${field.name}
|
||||
{
|
||||
id
|
||||
${relationMetadataItem?.fields
|
||||
.filter((field) => field.type !== 'RELATION')
|
||||
.map((field) => field.name)
|
||||
.join('\n')}
|
||||
}`;
|
||||
} else if (
|
||||
fieldType === 'RELATION' &&
|
||||
field.fromRelationMetadata?.relationType === 'ONE_TO_MANY'
|
||||
) {
|
||||
return `${field.name}
|
||||
{
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}`;
|
||||
} else if (fieldIsURL) {
|
||||
return `
|
||||
${field.name}
|
||||
{
|
||||
text
|
||||
link
|
||||
}
|
||||
`;
|
||||
} else if (fieldIsMoneyAmount) {
|
||||
return `
|
||||
${field.name}
|
||||
{
|
||||
amount
|
||||
currency
|
||||
}
|
||||
`;
|
||||
}
|
||||
};
|
||||
|
||||
return mapFieldMetadataToGraphQLQuery;
|
||||
};
|
||||
@ -1,65 +0,0 @@
|
||||
import { FieldType } from '@/ui/object/field/types/FieldType';
|
||||
|
||||
import { FieldMetadataItem } from '../types/FieldMetadataItem';
|
||||
|
||||
export const mapFieldMetadataToGraphQLQuery = (field: FieldMetadataItem) => {
|
||||
// TODO: parse
|
||||
const fieldType = field.type as FieldType;
|
||||
|
||||
const fieldIsSimpleValue = (
|
||||
[
|
||||
'TEXT',
|
||||
'PHONE',
|
||||
'DATE',
|
||||
'EMAIL',
|
||||
'NUMBER',
|
||||
'BOOLEAN',
|
||||
'DATE',
|
||||
] as FieldType[]
|
||||
).includes(fieldType);
|
||||
|
||||
const fieldIsURL = fieldType === 'URL' || fieldType === 'URL_V2';
|
||||
|
||||
const fieldIsMoneyAmount =
|
||||
fieldType === 'MONEY' || fieldType === 'MONEY_AMOUNT_V2';
|
||||
|
||||
if (fieldIsSimpleValue) {
|
||||
return field.name;
|
||||
} else if (
|
||||
fieldType === 'RELATION' &&
|
||||
field.toRelationMetadata?.relationType === 'ONE_TO_MANY'
|
||||
) {
|
||||
return `${field.name}
|
||||
{
|
||||
id
|
||||
}`;
|
||||
} else if (
|
||||
fieldType === 'RELATION' &&
|
||||
field.fromRelationMetadata?.relationType === 'ONE_TO_MANY'
|
||||
) {
|
||||
return `${field.name}
|
||||
{
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}`;
|
||||
} else if (fieldIsURL) {
|
||||
return `
|
||||
${field.name}
|
||||
{
|
||||
text
|
||||
link
|
||||
}
|
||||
`;
|
||||
} else if (fieldIsMoneyAmount) {
|
||||
return `
|
||||
${field.name}
|
||||
{
|
||||
amount
|
||||
currency
|
||||
}
|
||||
`;
|
||||
}
|
||||
};
|
||||
@ -2,8 +2,8 @@ import { ObjectMetadataItemsQuery } from '~/generated-metadata/graphql';
|
||||
|
||||
import { ObjectMetadataItem } from '../types/ObjectMetadataItem';
|
||||
|
||||
export const formatPagedObjectMetadataItemsToObjectMetadataItems = ({
|
||||
pagedObjectMetadataItems: pagedObjectMetadataItems,
|
||||
export const mapPaginatedObjectMetadataItemsToObjectMetadataItems = ({
|
||||
pagedObjectMetadataItems,
|
||||
}: {
|
||||
pagedObjectMetadataItems: ObjectMetadataItemsQuery | undefined;
|
||||
}) => {
|
||||
@ -0,0 +1,11 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const CREATE_ONE_WORKSPACE_MEMBER_V2 = gql`
|
||||
mutation CreateOneWorkspaceMemberV2($input: WorkspaceMemberV2CreateInput!) {
|
||||
createWorkspaceMemberV2(data: $input) {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -0,0 +1,15 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const FIND_ONE_WORKSPACE_MEMBER_V2 = gql`
|
||||
query FindManyWorkspaceMembersV2($filter: WorkspaceMemberV2FilterInput) {
|
||||
workspaceMembersV2(filter: $filter) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -20,12 +20,10 @@ export const useDeleteOneObjectRecord = ({
|
||||
const [mutate] = useMutation(deleteOneMutation);
|
||||
|
||||
const deleteOneObject = foundObjectMetadataItem
|
||||
? (input: Record<string, any>) => {
|
||||
? (idToDelete: string) => {
|
||||
return mutate({
|
||||
variables: {
|
||||
input: {
|
||||
...input,
|
||||
},
|
||||
idToDelete,
|
||||
},
|
||||
refetchQueries: [getOperationName(findManyQuery) ?? ''],
|
||||
});
|
||||
|
||||
@ -18,7 +18,7 @@ import {
|
||||
PaginatedObjectTypeEdge,
|
||||
PaginatedObjectTypeResults,
|
||||
} from '../types/PaginatedObjectTypeResults';
|
||||
import { formatPagedObjectsToObjects } from '../utils/formatPagedObjectsToObjects';
|
||||
import { mapPaginatedObjectsToObjects } from '../utils/mapPaginatedObjectsToObjects';
|
||||
|
||||
// TODO: test with a wrong name
|
||||
// TODO: add zod to validate that we have at least id on each object
|
||||
@ -163,7 +163,7 @@ export const useFindManyObjectRecords = <
|
||||
const objects = useMemo(
|
||||
() =>
|
||||
objectNamePlural
|
||||
? formatPagedObjectsToObjects({
|
||||
? mapPaginatedObjectsToObjects({
|
||||
pagedObjects: data,
|
||||
objectNamePlural,
|
||||
})
|
||||
|
||||
@ -16,6 +16,8 @@ export const useUpdateOneObjectRecord = ({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
console.log('update one object');
|
||||
|
||||
// TODO: type this with a minimal type at least with Record<string, any>
|
||||
const [mutate] = useMutation(updateOneMutation);
|
||||
|
||||
|
||||
@ -1,14 +1,21 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { EMPTY_MUTATION } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useMapFieldMetadataToGraphQLQuery } from '@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { mapFieldMetadataToGraphQLQuery } from '@/object-metadata/utils/mapFieldMetadataToGraphQLQuery';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const generateCreateOneObjectMutation = ({
|
||||
export const useGenerateCreateOneObjectMutation = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
objectMetadataItem: ObjectMetadataItem | undefined | null;
|
||||
}) => {
|
||||
const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery();
|
||||
|
||||
if (!objectMetadataItem) {
|
||||
return EMPTY_MUTATION;
|
||||
}
|
||||
|
||||
const capitalizedObjectName = capitalize(objectMetadataItem.nameSingular);
|
||||
|
||||
return gql`
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
export const formatPagedObjectsToObjects = <
|
||||
export const mapPaginatedObjectsToObjects = <
|
||||
ObjectType extends { id: string } & Record<string, any>,
|
||||
ObjectTypeQuery extends {
|
||||
[objectNamePlural: string]: {
|
||||
@ -1,14 +1,19 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { EMPTY_MUTATION } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const generateDeleteOneObjectMutation = ({
|
||||
export const useGenerateDeleteOneObjectMutation = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
objectMetadataItem: ObjectMetadataItem | undefined | null;
|
||||
}) => {
|
||||
const capitalizedObjectName = capitalize(objectMetadataItem.nameSingular);
|
||||
if (!objectMetadataItem) {
|
||||
return EMPTY_MUTATION;
|
||||
}
|
||||
|
||||
const capitalizedObjectName = capitalize(objectMetadataItem?.nameSingular);
|
||||
|
||||
return gql`
|
||||
mutation DeleteOne${capitalizedObjectName}($idToDelete: ID!) {
|
||||
@ -1,14 +1,21 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { EMPTY_QUERY } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useMapFieldMetadataToGraphQLQuery } from '@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { mapFieldMetadataToGraphQLQuery } from '@/object-metadata/utils/mapFieldMetadataToGraphQLQuery';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const generateFindManyCustomObjectsQuery = ({
|
||||
export const useGenerateFindManyCustomObjectsQuery = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
objectMetadataItem: ObjectMetadataItem | undefined | null;
|
||||
}) => {
|
||||
const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery();
|
||||
|
||||
if (!objectMetadataItem) {
|
||||
return EMPTY_QUERY;
|
||||
}
|
||||
|
||||
return gql`
|
||||
query FindMany${capitalize(
|
||||
objectMetadataItem.namePlural,
|
||||
@ -1,13 +1,20 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { EMPTY_QUERY } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useMapFieldMetadataToGraphQLQuery } from '@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { mapFieldMetadataToGraphQLQuery } from '@/object-metadata/utils/mapFieldMetadataToGraphQLQuery';
|
||||
|
||||
export const generateFindOneCustomObjectQuery = ({
|
||||
export const useGenerateFindOneCustomObjectQuery = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
objectMetadataItem: ObjectMetadataItem | null | undefined;
|
||||
}) => {
|
||||
const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery();
|
||||
|
||||
if (!objectMetadataItem) {
|
||||
return EMPTY_QUERY;
|
||||
}
|
||||
|
||||
return gql`
|
||||
query FindOne${objectMetadataItem.nameSingular}($objectMetadataId: UUID!) {
|
||||
${objectMetadataItem.nameSingular}(filter: {
|
||||
@ -1,7 +1,8 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { EMPTY_MUTATION } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useMapFieldMetadataToGraphQLQuery } from '@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { mapFieldMetadataToGraphQLQuery } from '@/object-metadata/utils/mapFieldMetadataToGraphQLQuery';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const getUpdateOneObjectMutationGraphQLField = ({
|
||||
@ -12,11 +13,17 @@ export const getUpdateOneObjectMutationGraphQLField = ({
|
||||
return `update${capitalize(objectNameSingular)}`;
|
||||
};
|
||||
|
||||
export const generateUpdateOneObjectMutation = ({
|
||||
export const useGenerateUpdateOneObjectMutation = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
objectMetadataItem: ObjectMetadataItem | undefined | null;
|
||||
}) => {
|
||||
const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery();
|
||||
|
||||
if (!objectMetadataItem) {
|
||||
return EMPTY_MUTATION;
|
||||
}
|
||||
|
||||
const capitalizedObjectName = capitalize(objectMetadataItem.nameSingular);
|
||||
|
||||
const graphQLFieldForUpdateOneObjectMutation =
|
||||
149
front/src/modules/search/hooks/useFilteredSearchEntityQueryV2.ts
Normal file
149
front/src/modules/search/hooks/useFilteredSearchEntityQueryV2.ts
Normal file
@ -0,0 +1,149 @@
|
||||
import { QueryHookOptions, QueryResult } from '@apollo/client';
|
||||
|
||||
import { mapPaginatedObjectsToObjects } from '@/object-record/utils/mapPaginatedObjectsToObjects';
|
||||
import { EntitiesForMultipleEntitySelect } from '@/ui/input/relation-picker/components/MultipleEntitySelect';
|
||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||
import { QueryMode, SortOrder } from '~/generated/graphql';
|
||||
|
||||
type SelectStringKeys<T> = NonNullable<
|
||||
{
|
||||
[K in keyof T]: K extends '__typename'
|
||||
? never
|
||||
: T[K] extends string | undefined | null
|
||||
? K
|
||||
: never;
|
||||
}[keyof T]
|
||||
>;
|
||||
|
||||
type ExtractEntityTypeFromQueryResponse<T> = T extends {
|
||||
searchResults: Array<infer U>;
|
||||
}
|
||||
? U
|
||||
: never;
|
||||
|
||||
type SearchFilter = { fieldNames: string[]; filter: string | number };
|
||||
|
||||
const DEFAULT_SEARCH_REQUEST_LIMIT = 10;
|
||||
|
||||
// TODO: use this for all search queries, because we need selectedEntities and entitiesToSelect each time we want to search
|
||||
// Filtered entities to select are
|
||||
export const useFilteredSearchEntityQueryV2 = ({
|
||||
queryHook,
|
||||
orderByField,
|
||||
filters,
|
||||
sortOrder = SortOrder.Asc,
|
||||
selectedIds,
|
||||
mappingFunction,
|
||||
limit,
|
||||
excludeEntityIds = [],
|
||||
objectNamePlural,
|
||||
}: {
|
||||
queryHook: (
|
||||
queryOptions?: QueryHookOptions<any, any>,
|
||||
) => QueryResult<any, any>;
|
||||
orderByField: string;
|
||||
filters: SearchFilter[];
|
||||
sortOrder?: SortOrder;
|
||||
selectedIds: string[];
|
||||
mappingFunction: (entity: any) => EntityForSelect;
|
||||
limit?: number;
|
||||
excludeEntityIds?: string[];
|
||||
objectNamePlural: string;
|
||||
}): EntitiesForMultipleEntitySelect<EntityForSelect> => {
|
||||
const { loading: selectedEntitiesLoading, data: selectedEntitiesData } =
|
||||
queryHook({
|
||||
variables: {
|
||||
where: {
|
||||
id: {
|
||||
in: selectedIds,
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
[orderByField]: sortOrder,
|
||||
},
|
||||
} as any,
|
||||
});
|
||||
|
||||
const searchFilter = filters.map(({ fieldNames, filter }) => {
|
||||
return {
|
||||
OR: fieldNames.map((fieldName) => ({
|
||||
[fieldName]: {
|
||||
startsWith: `%${filter}%`,
|
||||
mode: QueryMode.Insensitive,
|
||||
},
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
const {
|
||||
loading: filteredSelectedEntitiesLoading,
|
||||
data: filteredSelectedEntitiesData,
|
||||
} = queryHook({
|
||||
variables: {
|
||||
where: {
|
||||
AND: [
|
||||
{
|
||||
AND: searchFilter,
|
||||
},
|
||||
{
|
||||
id: {
|
||||
in: selectedIds,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
orderBy: {
|
||||
[orderByField]: sortOrder,
|
||||
},
|
||||
} as any,
|
||||
});
|
||||
|
||||
const { loading: entitiesToSelectLoading, data: entitiesToSelectData } =
|
||||
queryHook({
|
||||
variables: {
|
||||
where: {
|
||||
AND: [
|
||||
{
|
||||
AND: searchFilter,
|
||||
},
|
||||
{
|
||||
id: {
|
||||
notIn: [...selectedIds, ...excludeEntityIds],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
limit: limit ?? DEFAULT_SEARCH_REQUEST_LIMIT,
|
||||
orderBy: {
|
||||
[orderByField]: sortOrder,
|
||||
},
|
||||
} as any,
|
||||
});
|
||||
|
||||
console.log({
|
||||
selectedEntitiesData,
|
||||
test: mapPaginatedObjectsToObjects({
|
||||
objectNamePlural: objectNamePlural,
|
||||
pagedObjects: selectedEntitiesData,
|
||||
}),
|
||||
});
|
||||
|
||||
return {
|
||||
selectedEntities: mapPaginatedObjectsToObjects({
|
||||
objectNamePlural: objectNamePlural,
|
||||
pagedObjects: selectedEntitiesData,
|
||||
}).map(mappingFunction),
|
||||
filteredSelectedEntities: mapPaginatedObjectsToObjects({
|
||||
objectNamePlural: objectNamePlural,
|
||||
pagedObjects: filteredSelectedEntitiesData,
|
||||
}).map(mappingFunction),
|
||||
entitiesToSelect: mapPaginatedObjectsToObjects({
|
||||
objectNamePlural: objectNamePlural,
|
||||
pagedObjects: entitiesToSelectData,
|
||||
}).map(mappingFunction),
|
||||
loading:
|
||||
entitiesToSelectLoading ||
|
||||
filteredSelectedEntitiesLoading ||
|
||||
selectedEntitiesLoading,
|
||||
};
|
||||
};
|
||||
@ -5,6 +5,7 @@ import debounce from 'lodash.debounce';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import { useUpdateUserMutation } from '~/generated/graphql';
|
||||
@ -30,9 +31,14 @@ export const NameFields = ({
|
||||
onLastNameUpdate,
|
||||
}: NameFieldsProps) => {
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
|
||||
const [firstName, setFirstName] = useState(currentUser?.firstName ?? '');
|
||||
const [lastName, setLastName] = useState(currentUser?.lastName ?? '');
|
||||
const [firstName, setFirstName] = useState(
|
||||
currentWorkspaceMember?.firstName ?? '',
|
||||
);
|
||||
const [lastName, setLastName] = useState(
|
||||
currentWorkspaceMember?.lastName ?? '',
|
||||
);
|
||||
|
||||
const [updateUser] = useUpdateUserMutation();
|
||||
|
||||
@ -69,13 +75,13 @@ export const NameFields = ({
|
||||
}, 500);
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentUser) {
|
||||
if (!currentWorkspaceMember) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
currentUser.firstName !== firstName ||
|
||||
currentUser.lastName !== lastName
|
||||
currentWorkspaceMember.firstName !== firstName ||
|
||||
currentWorkspaceMember.lastName !== lastName
|
||||
) {
|
||||
debouncedUpdate();
|
||||
}
|
||||
@ -83,7 +89,14 @@ export const NameFields = ({
|
||||
return () => {
|
||||
debouncedUpdate.cancel();
|
||||
};
|
||||
}, [firstName, lastName, currentUser, debouncedUpdate, autoSave]);
|
||||
}, [
|
||||
firstName,
|
||||
lastName,
|
||||
currentUser,
|
||||
debouncedUpdate,
|
||||
autoSave,
|
||||
currentWorkspaceMember,
|
||||
]);
|
||||
|
||||
return (
|
||||
<StyledComboInputContainer>
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { useState } from 'react';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { ImageInput } from '@/ui/input/components/ImageInput';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import { getImageAbsoluteURIOrBase64 } from '@/users/utils/getProfilePictureAbsoluteURI';
|
||||
@ -16,6 +17,8 @@ export const ProfilePictureUploader = () => {
|
||||
useUploadProfilePictureMutation();
|
||||
const [removePicture] = useRemoveProfilePictureMutation();
|
||||
const [currentUser] = useRecoilState(currentUserState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
|
||||
const [uploadController, setUploadController] =
|
||||
useState<AbortController | null>(null);
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
@ -69,7 +72,7 @@ export const ProfilePictureUploader = () => {
|
||||
|
||||
return (
|
||||
<ImageInput
|
||||
picture={getImageAbsoluteURIOrBase64(currentUser?.avatarUrl)}
|
||||
picture={getImageAbsoluteURIOrBase64(currentWorkspaceMember?.avatarUrl)}
|
||||
onUpload={handleUpload}
|
||||
onRemove={handleRemove}
|
||||
onAbort={handleAbort}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar/hooks/useSnackBar';
|
||||
import { Toggle } from '@/ui/input/components/Toggle';
|
||||
import { useUpdateAllowImpersonationMutation } from '~/generated/graphql';
|
||||
@ -8,7 +8,7 @@ import { useUpdateAllowImpersonationMutation } from '~/generated/graphql';
|
||||
export const ToggleField = () => {
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
|
||||
const [updateAllowImpersonation] = useUpdateAllowImpersonationMutation();
|
||||
|
||||
@ -32,7 +32,7 @@ export const ToggleField = () => {
|
||||
|
||||
return (
|
||||
<Toggle
|
||||
value={currentUser?.workspaceMember?.allowImpersonation}
|
||||
value={currentWorkspaceMember?.allowImpersonation}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -2,9 +2,9 @@ import { useCallback, useEffect, useState } from 'react';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import styled from '@emotion/styled';
|
||||
import debounce from 'lodash.debounce';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import { useUpdateWorkspaceMutation } from '~/generated/graphql';
|
||||
@ -27,10 +27,11 @@ export const NameField = ({
|
||||
autoSave = true,
|
||||
onNameUpdate,
|
||||
}: NameFieldProps) => {
|
||||
const [currentUser] = useRecoilState(currentUserState);
|
||||
const workspace = currentUser?.workspaceMember?.workspace;
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
|
||||
const [displayName, setDisplayName] = useState(workspace?.displayName ?? '');
|
||||
const [displayName, setDisplayName] = useState(
|
||||
currentWorkspace?.displayName ?? '',
|
||||
);
|
||||
|
||||
const [updateWorkspace] = useUpdateWorkspaceMutation();
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { ImageInput } from '@/ui/input/components/ImageInput';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import { getImageAbsoluteURIOrBase64 } from '@/users/utils/getProfilePictureAbsoluteURI';
|
||||
@ -13,7 +13,8 @@ import {
|
||||
export const WorkspaceLogoUploader = () => {
|
||||
const [uploadLogo] = useUploadWorkspaceLogoMutation();
|
||||
const [removeLogo] = useRemoveWorkspaceLogoMutation();
|
||||
const [currentUser] = useRecoilState(currentUserState);
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
|
||||
const onUpload = async (file: File) => {
|
||||
if (!file) {
|
||||
return;
|
||||
@ -34,9 +35,7 @@ export const WorkspaceLogoUploader = () => {
|
||||
|
||||
return (
|
||||
<ImageInput
|
||||
picture={getImageAbsoluteURIOrBase64(
|
||||
currentUser?.workspaceMember?.workspace.logo,
|
||||
)}
|
||||
picture={getImageAbsoluteURIOrBase64(currentWorkspace?.logo)}
|
||||
onUpload={onUpload}
|
||||
onRemove={onRemove}
|
||||
/>
|
||||
|
||||
@ -8,7 +8,7 @@ import { Avatar, AvatarType } from '@/users/components/Avatar';
|
||||
|
||||
import { Chip, ChipVariant } from './Chip';
|
||||
|
||||
type EntityChipProps = {
|
||||
export type EntityChipProps = {
|
||||
linkToEntity?: string;
|
||||
entityId: string;
|
||||
name: string;
|
||||
|
||||
@ -62,9 +62,13 @@ export const SingleEntitySelectBase = <
|
||||
|
||||
const entitiesInDropdown = [selectedEntity, ...entitiesToSelect].filter(
|
||||
(entity): entity is CustomEntityForSelect =>
|
||||
assertNotNull(entity) && isNonEmptyString(entity.name.trim()),
|
||||
assertNotNull(entity) && isNonEmptyString(entity.name),
|
||||
);
|
||||
|
||||
console.log({
|
||||
entitiesInDropdown,
|
||||
});
|
||||
|
||||
const { preselectedOptionId, resetScroll } = useEntitySelectScroll({
|
||||
selectableOptionIds: [
|
||||
EmptyButtonId,
|
||||
|
||||
@ -5,6 +5,7 @@ export enum Entity {
|
||||
Company = 'Company',
|
||||
Person = 'Person',
|
||||
User = 'User',
|
||||
WorkspaceMember = 'WorkspaceMember',
|
||||
}
|
||||
|
||||
export type EntityTypeForSelect =
|
||||
|
||||
@ -2,6 +2,7 @@ import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { getImageAbsoluteURIOrBase64 } from '@/users/utils/getProfilePictureAbsoluteURI';
|
||||
|
||||
import NavCollapseButton from './NavCollapseButton';
|
||||
@ -53,8 +54,8 @@ const NavWorkspaceButton = ({
|
||||
showCollapseButton,
|
||||
}: NavWorkspaceButtonProps) => {
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
|
||||
const currentWorkspace = currentUser?.workspaceMember?.workspace;
|
||||
const DEFAULT_LOGO =
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACb0lEQVR4nO2VO4taQRTHr3AblbjxEVlwCwVhg7BoqqCIjy/gAyyFWNlYBOxsfH0KuxgQGwXRUkGuL2S7i1barGAgiwbdW93SnGOc4BonPiKahf3DwXFmuP/fPM4ZlvmlTxAhCBdzHnEQWYiv7Mr4C3NeuVYhQYDPzOUUQgDLBQGcLHNhvQK8DACPx8PTxiqVyvISG43GbyaT6Qfpn06n0m63e/tPAPF4vJ1MJu8kEsnWTCkWi1yr1RKGw+GDRqPBOTfr44vFQvD7/Q/lcpmaaVQAr9fLp1IpO22c47hGOBz+MB6PH+Vy+VYDAL8qlUoGtVotzOfzq4MAgsHgE/6KojiQyWR/bKVSqbSszHFM8Pl8z1YK48JsNltCOBwOnrYLO+8AAIjb+nHbycoTiUQfDJ7tFq4YAHiVSmXBxcD41u8flQU8z7fhzO0r83atVns3Go3u9Xr9x0O/RQXo9/tsIBBg6vX606a52Wz+bZ7P5/WwG29gxSJzhKgA6XTaDoFNF+krFAocmC//4yWEcSf2wTm7mCO19xFgSsKOLI16vV7b7XY7mRNoLwA0JymJ5uQIzgIAuX5PzDElT2m+E8BqtQ4ymcx7Yq7T6a6ZE4sKgOadTucaCwkxp1UzlEKh0GDxIXOwDWHAdi6Xe3swQDQa/Q7mywoolUpvsaptymazDWKxmBHTlWXZm405BFZoNpuGgwEmk4mE2SGtVivii4f1AO7J3ZopkQCQj7Ar1FeRChCJRJzVapX6DKNIfSc1Ax+wtQWQ55h6bH8FWDfYV4fO3wlwDr0C/BcADYiTPCxHqIEA2QsCZAkAKnRGkMbKN/sTX5YHPQ1e7SkAAAAASUVORK5CYII=';
|
||||
|
||||
|
||||
@ -3,6 +3,10 @@ import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import {
|
||||
CurrentWorkspaceMember,
|
||||
currentWorkspaceMemberState,
|
||||
} from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { supportChatState } from '@/client-config/states/supportChatState';
|
||||
import { IconHelpCircle } from '@/ui/display/icon';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
@ -30,13 +34,18 @@ const insertScript = ({
|
||||
|
||||
const SupportChat = () => {
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const supportChat = useRecoilValue(supportChatState);
|
||||
const [isFrontChatLoaded, setIsFrontChatLoaded] = useState(false);
|
||||
|
||||
const configureFront = useCallback(
|
||||
(
|
||||
chatId: string,
|
||||
currentUser: Pick<User, 'email' | 'displayName' | 'supportUserHash'>,
|
||||
currentUser: Pick<User, 'email' | 'supportUserHash'>,
|
||||
currentWorkspaceMember: Pick<
|
||||
CurrentWorkspaceMember,
|
||||
'firstName' | 'lastName'
|
||||
>,
|
||||
) => {
|
||||
const url = 'https://chat-assets.frontapp.com/v1/chat.bundle.js';
|
||||
const script = document.querySelector(`script[src="${url}"]`);
|
||||
@ -49,7 +58,10 @@ const SupportChat = () => {
|
||||
chatId,
|
||||
useDefaultLauncher: false,
|
||||
email: currentUser.email,
|
||||
name: currentUser.displayName,
|
||||
name:
|
||||
currentWorkspaceMember.firstName +
|
||||
' ' +
|
||||
currentWorkspaceMember.lastName,
|
||||
userHash: currentUser?.supportUserHash,
|
||||
});
|
||||
setIsFrontChatLoaded(true);
|
||||
@ -65,9 +77,14 @@ const SupportChat = () => {
|
||||
supportChat?.supportDriver === 'front' &&
|
||||
supportChat.supportFrontChatId &&
|
||||
currentUser?.email &&
|
||||
currentWorkspaceMember &&
|
||||
!isFrontChatLoaded
|
||||
) {
|
||||
configureFront(supportChat.supportFrontChatId, currentUser);
|
||||
configureFront(
|
||||
supportChat.supportFrontChatId,
|
||||
currentUser,
|
||||
currentWorkspaceMember,
|
||||
);
|
||||
}
|
||||
}, [
|
||||
configureFront,
|
||||
@ -75,6 +92,7 @@ const SupportChat = () => {
|
||||
isFrontChatLoaded,
|
||||
supportChat?.supportDriver,
|
||||
supportChat.supportFrontChatId,
|
||||
currentWorkspaceMember,
|
||||
]);
|
||||
|
||||
return isFrontChatLoaded ? (
|
||||
|
||||
@ -105,13 +105,16 @@ export const usePersistField = () => {
|
||||
valueToPersist,
|
||||
);
|
||||
|
||||
console.log({
|
||||
fieldName,
|
||||
valueToPersist,
|
||||
});
|
||||
|
||||
updateEntity?.({
|
||||
variables: {
|
||||
where: { id: entityId },
|
||||
data: {
|
||||
[fieldName]: valueToPersist
|
||||
? { connect: { id: valueToPersist.id } }
|
||||
: { disconnect: true },
|
||||
[fieldName]: valueToPersist?.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,24 +1,27 @@
|
||||
import { EntityChip } from '@/ui/display/chip/components/EntityChip';
|
||||
import { getEntityChipFromFieldMetadata } from '@/ui/object/field/meta-types/display/utils/getEntityChipFromFieldMetadata';
|
||||
|
||||
import { useRelationField } from '../../hooks/useRelationField';
|
||||
|
||||
export const RelationFieldDisplay = () => {
|
||||
const { fieldValue, fieldDefinition } = useRelationField();
|
||||
const { entityChipDisplayMapper } = fieldDefinition;
|
||||
if (!entityChipDisplayMapper) {
|
||||
throw new Error(
|
||||
"Missing entityChipDisplayMapper in FieldContext. Please provide it in the FieldContextProvider's value prop.",
|
||||
);
|
||||
}
|
||||
const { name, pictureUrl, avatarType } =
|
||||
entityChipDisplayMapper?.(fieldValue);
|
||||
|
||||
console.log({
|
||||
fieldDefinition,
|
||||
fieldValue,
|
||||
});
|
||||
|
||||
const entityChipProps = getEntityChipFromFieldMetadata(
|
||||
fieldDefinition,
|
||||
fieldValue,
|
||||
);
|
||||
|
||||
return (
|
||||
<EntityChip
|
||||
entityId={fieldValue?.id}
|
||||
name={name}
|
||||
pictureUrl={pictureUrl}
|
||||
avatarType={avatarType}
|
||||
entityId={entityChipProps.entityId}
|
||||
name={entityChipProps.name}
|
||||
pictureUrl={entityChipProps.pictureUrl}
|
||||
avatarType={entityChipProps.avatarType}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
import { EntityChipProps } from '@/ui/display/chip/components/EntityChip';
|
||||
import { FieldDefinition } from '@/ui/object/field/types/FieldDefinition';
|
||||
import { FieldRelationMetadata } from '@/ui/object/field/types/FieldMetadata';
|
||||
|
||||
export const getEntityChipFromFieldMetadata = (
|
||||
fieldDefinition: FieldDefinition<FieldRelationMetadata>,
|
||||
fieldValue: any,
|
||||
) => {
|
||||
const { fieldName } = fieldDefinition.metadata;
|
||||
|
||||
const chipValue: Pick<
|
||||
EntityChipProps,
|
||||
'name' | 'pictureUrl' | 'avatarType' | 'entityId'
|
||||
> = {
|
||||
name: '',
|
||||
pictureUrl: '',
|
||||
avatarType: 'rounded',
|
||||
entityId: fieldValue?.id,
|
||||
};
|
||||
|
||||
console.log({
|
||||
fieldName,
|
||||
fieldValue,
|
||||
});
|
||||
|
||||
// TODO: use every
|
||||
if (fieldName === 'accountOwner' && fieldValue) {
|
||||
chipValue.name = fieldValue.firstName + ' ' + fieldValue.lastName;
|
||||
}
|
||||
|
||||
return chipValue;
|
||||
};
|
||||
@ -22,6 +22,11 @@ export const useRelationField = () => {
|
||||
}),
|
||||
);
|
||||
|
||||
console.log({
|
||||
fieldDefinition,
|
||||
fieldValue,
|
||||
});
|
||||
|
||||
const fieldInitialValue = useFieldInitialValue();
|
||||
|
||||
const initialSearchValue = fieldInitialValue?.isEmpty
|
||||
|
||||
@ -40,7 +40,7 @@ export const RelationFieldInput = ({
|
||||
|
||||
return (
|
||||
<StyledRelationPickerContainer>
|
||||
{fieldDefinition.metadata.relationType === Entity.Person ? (
|
||||
{fieldDefinition.metadata.fieldName === 'person' ? (
|
||||
<PeoplePicker
|
||||
personId={initialValue?.id ?? ''}
|
||||
companyId={initialValue?.companyId ?? ''}
|
||||
@ -48,7 +48,7 @@ export const RelationFieldInput = ({
|
||||
onCancel={onCancel}
|
||||
initialSearchFilter={initialSearchValue}
|
||||
/>
|
||||
) : fieldDefinition.metadata.relationType === Entity.User ? (
|
||||
) : fieldDefinition.metadata.fieldName === 'accountOwner' ? (
|
||||
<UserPicker
|
||||
userId={initialValue?.id ?? ''}
|
||||
onSubmit={handleSubmit}
|
||||
|
||||
@ -82,6 +82,8 @@ type RecordTableProps = {
|
||||
export const RecordTable = ({ updateEntityMutation }: RecordTableProps) => {
|
||||
const tableBodyRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
console.log('record table');
|
||||
|
||||
const {
|
||||
leaveTableFocus,
|
||||
setRowSelectedState,
|
||||
|
||||
@ -1,105 +1,32 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import {
|
||||
ColorScheme,
|
||||
useUpdateOneWorkspaceMemberMutation,
|
||||
useUpdateUserMutation,
|
||||
} from '~/generated/graphql';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord';
|
||||
import { ColorScheme } from '~/generated/graphql';
|
||||
|
||||
export const useColorScheme = () => {
|
||||
const [currentUser, setCurrentUser] = useRecoilState(currentUserState);
|
||||
const [currentWorkspaceMember] = useRecoilState(currentWorkspaceMemberState);
|
||||
|
||||
const [updateUser] = useUpdateUserMutation();
|
||||
const [updateWorkspaceMember] = useUpdateOneWorkspaceMemberMutation();
|
||||
|
||||
const colorScheme =
|
||||
!currentUser?.workspaceMember.settings?.colorScheme &&
|
||||
!currentUser?.settings?.colorScheme
|
||||
? ColorScheme.System
|
||||
: currentUser.workspaceMember.settings?.colorScheme ??
|
||||
currentUser.settings.colorScheme;
|
||||
const { updateOneObject: updateOneWorkspaceMember } =
|
||||
useUpdateOneObjectRecord({
|
||||
objectNamePlural: 'workspaceMembersV2',
|
||||
});
|
||||
const colorScheme = currentWorkspaceMember?.colorScheme ?? ColorScheme.System;
|
||||
|
||||
const setColorScheme = useCallback(
|
||||
async (value: ColorScheme) => {
|
||||
try {
|
||||
// connect settings to workspace member if not already connected
|
||||
await updateWorkspaceMember({
|
||||
variables: {
|
||||
where: { id: currentUser?.workspaceMember.id },
|
||||
data: { settings: { connect: { id: currentUser?.settings.id } } },
|
||||
},
|
||||
});
|
||||
|
||||
const result = await updateUser({
|
||||
variables: {
|
||||
where: {
|
||||
id: currentUser?.id,
|
||||
},
|
||||
data: {
|
||||
settings: {
|
||||
update: {
|
||||
colorScheme: value,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
optimisticResponse: currentUser
|
||||
? {
|
||||
__typename: 'Mutation',
|
||||
updateUser: {
|
||||
__typename: 'User',
|
||||
...currentUser,
|
||||
workspaceMember: {
|
||||
...currentUser.workspaceMember,
|
||||
settings: {
|
||||
__typename: 'UserSettings',
|
||||
id: currentUser.settings.id,
|
||||
colorScheme: value,
|
||||
locale: currentUser.settings.locale,
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
__typename: 'UserSettings',
|
||||
id: currentUser.settings.id,
|
||||
colorScheme: value,
|
||||
locale: currentUser.settings.locale,
|
||||
},
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
update: (_cache, { data }) => {
|
||||
if (
|
||||
data?.updateUser.workspaceMember?.settings?.colorScheme &&
|
||||
currentUser
|
||||
) {
|
||||
setCurrentUser({
|
||||
...currentUser,
|
||||
workspaceMember: {
|
||||
...currentUser.workspaceMember,
|
||||
settings: {
|
||||
...currentUser.workspaceMember.settings,
|
||||
colorScheme:
|
||||
data.updateUser.workspaceMember.settings.colorScheme,
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
...currentUser.settings,
|
||||
colorScheme:
|
||||
data.updateUser.workspaceMember.settings.colorScheme,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (!result.data || result.errors) {
|
||||
throw result.errors;
|
||||
}
|
||||
} catch (err) {}
|
||||
if (!currentWorkspaceMember) {
|
||||
return;
|
||||
}
|
||||
await updateOneWorkspaceMember?.({
|
||||
idToUpdate: currentWorkspaceMember?.id,
|
||||
input: {
|
||||
colorScheme: value,
|
||||
},
|
||||
});
|
||||
},
|
||||
[updateWorkspaceMember, currentUser, updateUser, setCurrentUser],
|
||||
[currentWorkspaceMember, updateOneWorkspaceMember],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
|
||||
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
|
||||
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useFilteredSearchEntityQueryV2 } from '@/search/hooks/useFilteredSearchEntityQueryV2';
|
||||
import { IconUserCircle } from '@/ui/display/icon';
|
||||
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
|
||||
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
|
||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useSearchUserQuery } from '~/generated/graphql';
|
||||
|
||||
export type UserPickerProps = {
|
||||
userId: string;
|
||||
@ -18,7 +19,7 @@ export type UserPickerProps = {
|
||||
};
|
||||
|
||||
type UserForSelect = EntityForSelect & {
|
||||
entityType: Entity.User;
|
||||
entityType: Entity.WorkspaceMember;
|
||||
};
|
||||
|
||||
export const UserPicker = ({
|
||||
@ -35,29 +36,39 @@ export const UserPicker = ({
|
||||
setRelationPickerSearchFilter(initialSearchFilter ?? '');
|
||||
}, [initialSearchFilter, setRelationPickerSearchFilter]);
|
||||
|
||||
const users = useFilteredSearchEntityQuery({
|
||||
queryHook: useSearchUserQuery,
|
||||
const { findManyQuery } = useFindOneObjectMetadataItem({
|
||||
objectNamePlural: 'workspaceMembersV2',
|
||||
});
|
||||
|
||||
const useFindManyWorkspaceMembers = () => useQuery(findManyQuery, {});
|
||||
|
||||
// TODO: put workspace member
|
||||
const users = useFilteredSearchEntityQueryV2({
|
||||
queryHook: useFindManyWorkspaceMembers,
|
||||
filters: [
|
||||
{
|
||||
fieldNames: ['firstName', 'lastName'],
|
||||
filter: relationPickerSearchFilter,
|
||||
},
|
||||
],
|
||||
orderByField: 'firstName',
|
||||
mappingFunction: (user) => ({
|
||||
entityType: Entity.User,
|
||||
id: user.id,
|
||||
name: user.displayName,
|
||||
orderByField: '',
|
||||
mappingFunction: (workspaceMember) => ({
|
||||
entityType: Entity.WorkspaceMember,
|
||||
id: workspaceMember.id,
|
||||
name: workspaceMember.firstName,
|
||||
avatarType: 'rounded',
|
||||
avatarUrl: user.avatarUrl ?? '',
|
||||
originalEntity: user,
|
||||
avatarUrl: '',
|
||||
originalEntity: workspaceMember,
|
||||
}),
|
||||
selectedIds: userId ? [userId] : [],
|
||||
objectNamePlural: 'workspaceMembersV2',
|
||||
});
|
||||
|
||||
const handleEntitySelected = async (
|
||||
selectedUser: UserForSelect | null | undefined,
|
||||
) => {
|
||||
console.log({
|
||||
users,
|
||||
});
|
||||
|
||||
const handleEntitySelected = async (selectedUser: any | null | undefined) => {
|
||||
onSubmit(selectedUser ?? null);
|
||||
};
|
||||
|
||||
|
||||
@ -1,29 +1,71 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { useGetCurrentUserQuery } from '~/generated/graphql';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { FIND_ONE_WORKSPACE_MEMBER_V2 } from '@/object-record/graphql/queries/findOneWorkspaceMember';
|
||||
import {
|
||||
useGetCurrentUserQuery,
|
||||
useGetCurrentWorkspaceQuery,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
export const UserProvider = ({ children }: React.PropsWithChildren) => {
|
||||
const [, setCurrentUser] = useRecoilState(currentUserState);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isWorkspaceMemberLoading, setIsWorkspaceMemberLoading] =
|
||||
useState(true);
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const { data, loading } = useGetCurrentUserQuery();
|
||||
const setCurrentUser = useSetRecoilState(currentUserState);
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
if (data?.currentUser?.workspaceMember?.settings) {
|
||||
setCurrentUser({
|
||||
...data.currentUser,
|
||||
workspaceMember: {
|
||||
...data.currentUser.workspaceMember,
|
||||
settings: data.currentUser.workspaceMember.settings,
|
||||
const { data: userData, loading: userLoading } = useGetCurrentUserQuery({
|
||||
onCompleted: async (data) => {
|
||||
const workspaceMember = await apolloClient.query({
|
||||
query: FIND_ONE_WORKSPACE_MEMBER_V2,
|
||||
variables: {
|
||||
filter: {
|
||||
userId: { eq: data.currentUser.id },
|
||||
},
|
||||
},
|
||||
});
|
||||
setCurrentWorkspaceMember(
|
||||
workspaceMember.data.workspaceMembersV2.edges[0].node,
|
||||
);
|
||||
setIsWorkspaceMemberLoading(false);
|
||||
},
|
||||
onError: () => {
|
||||
setIsWorkspaceMemberLoading(false);
|
||||
},
|
||||
});
|
||||
|
||||
const { data: workspaceData, loading: workspaceLoading } =
|
||||
useGetCurrentWorkspaceQuery();
|
||||
|
||||
useEffect(() => {
|
||||
if (!userLoading && !workspaceLoading && !isWorkspaceMemberLoading) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [setCurrentUser, data, isLoading, loading]);
|
||||
if (userData?.currentUser) {
|
||||
setCurrentUser(userData.currentUser);
|
||||
}
|
||||
if (workspaceData?.currentWorkspace) {
|
||||
setCurrentWorkspace(workspaceData.currentWorkspace);
|
||||
}
|
||||
}, [
|
||||
setCurrentUser,
|
||||
isLoading,
|
||||
userLoading,
|
||||
workspaceLoading,
|
||||
userData?.currentUser,
|
||||
workspaceData?.currentWorkspace,
|
||||
setCurrentWorkspace,
|
||||
isWorkspaceMemberLoading,
|
||||
]);
|
||||
|
||||
return isLoading ? <></> : <>{children}</>;
|
||||
};
|
||||
|
||||
@ -5,52 +5,6 @@ export const UPDATE_USER = gql`
|
||||
updateUser(data: $data, where: $where) {
|
||||
id
|
||||
email
|
||||
displayName
|
||||
firstName
|
||||
lastName
|
||||
avatarUrl
|
||||
workspaceMember {
|
||||
id
|
||||
workspace {
|
||||
id
|
||||
domainName
|
||||
displayName
|
||||
logo
|
||||
inviteHash
|
||||
}
|
||||
assignedActivities {
|
||||
id
|
||||
title
|
||||
}
|
||||
authoredActivities {
|
||||
id
|
||||
title
|
||||
}
|
||||
authoredAttachments {
|
||||
id
|
||||
name
|
||||
type
|
||||
}
|
||||
settings {
|
||||
id
|
||||
colorScheme
|
||||
locale
|
||||
}
|
||||
companies {
|
||||
id
|
||||
name
|
||||
domainName
|
||||
}
|
||||
comments {
|
||||
id
|
||||
body
|
||||
}
|
||||
}
|
||||
settings {
|
||||
id
|
||||
locale
|
||||
colorScheme
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@ -4,16 +4,7 @@ export const GET_CURRENT_USER = gql`
|
||||
query GetCurrentUser {
|
||||
currentUser {
|
||||
...userFieldsFragment
|
||||
avatarUrl
|
||||
canImpersonate
|
||||
workspaceMember {
|
||||
...workspaceMemberFieldsFragment
|
||||
}
|
||||
settings {
|
||||
id
|
||||
locale
|
||||
colorScheme
|
||||
}
|
||||
supportUserHash
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
export type WorkspaceMember = {
|
||||
id: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
avatarUrl: string;
|
||||
};
|
||||
@ -2,7 +2,7 @@ import styled from '@emotion/styled';
|
||||
|
||||
import { OverflowingTextWithTooltip } from '@/ui/display/tooltip/OverflowingTextWithTooltip';
|
||||
import { Avatar } from '@/users/components/Avatar';
|
||||
import { User } from '~/generated/graphql';
|
||||
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
@ -29,12 +29,7 @@ const StyledEmailText = styled.span`
|
||||
`;
|
||||
|
||||
type WorkspaceMemberCardProps = {
|
||||
workspaceMember: {
|
||||
user: Pick<
|
||||
User,
|
||||
'id' | 'firstName' | 'lastName' | 'displayName' | 'avatarUrl' | 'email'
|
||||
>;
|
||||
};
|
||||
workspaceMember: WorkspaceMember;
|
||||
accessory?: React.ReactNode;
|
||||
};
|
||||
|
||||
@ -44,15 +39,19 @@ export const WorkspaceMemberCard = ({
|
||||
}: WorkspaceMemberCardProps) => (
|
||||
<StyledContainer>
|
||||
<Avatar
|
||||
avatarUrl={workspaceMember.user.avatarUrl}
|
||||
colorId={workspaceMember.user.id}
|
||||
placeholder={workspaceMember.user.firstName || ''}
|
||||
avatarUrl={workspaceMember.avatarUrl}
|
||||
colorId={workspaceMember.id}
|
||||
placeholder={workspaceMember.firstName || ''}
|
||||
type="squared"
|
||||
size="xl"
|
||||
/>
|
||||
<StyledContent>
|
||||
<OverflowingTextWithTooltip text={workspaceMember.user.displayName} />
|
||||
<StyledEmailText>{workspaceMember.user.email}</StyledEmailText>
|
||||
<OverflowingTextWithTooltip
|
||||
text={workspaceMember.firstName + ' ' + workspaceMember.lastName}
|
||||
/>
|
||||
<StyledEmailText>
|
||||
{workspaceMember.firstName + ' ' + workspaceMember.lastName}
|
||||
</StyledEmailText>
|
||||
</StyledContent>
|
||||
|
||||
{accessory}
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const WORKSPACE_MEMBER_FIELDS_FRAGMENT = gql`
|
||||
fragment workspaceMemberFieldsFragment on WorkspaceMember {
|
||||
id
|
||||
allowImpersonation
|
||||
workspace {
|
||||
id
|
||||
domainName
|
||||
displayName
|
||||
logo
|
||||
inviteHash
|
||||
}
|
||||
assignedActivities {
|
||||
id
|
||||
title
|
||||
}
|
||||
authoredActivities {
|
||||
id
|
||||
title
|
||||
}
|
||||
authoredAttachments {
|
||||
id
|
||||
name
|
||||
type
|
||||
}
|
||||
settings {
|
||||
id
|
||||
colorScheme
|
||||
locale
|
||||
}
|
||||
companies {
|
||||
id
|
||||
name
|
||||
domainName
|
||||
}
|
||||
comments {
|
||||
id
|
||||
body
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -1,9 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const REMOVE_WORKSPACE_MEMBER = gql`
|
||||
mutation RemoveWorkspaceMember($where: WorkspaceMemberWhereUniqueInput!) {
|
||||
deleteWorkspaceMember(where: $where) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -1,12 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const UPDATE_WORKSPACE_MEMBER = gql`
|
||||
mutation UpdateOneWorkspaceMember(
|
||||
$data: WorkspaceMemberUpdateInput!
|
||||
$where: WorkspaceMemberWhereUniqueInput!
|
||||
) {
|
||||
UpdateOneWorkspaceMember(data: $data, where: $where) {
|
||||
...workspaceMemberFieldsFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -0,0 +1,11 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_CURRENT_WORKSPACE = gql`
|
||||
query getCurrentWorkspace {
|
||||
currentWorkspace {
|
||||
id
|
||||
displayName
|
||||
logo
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -4,10 +4,6 @@ export const GET_WORKSPACE_MEMBERS = gql`
|
||||
query GetWorkspaceMembers($where: WorkspaceMemberWhereInput) {
|
||||
workspaceMembers: findManyWorkspaceMember(where: $where) {
|
||||
id
|
||||
user {
|
||||
...userFieldsFragment
|
||||
avatarUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user