Favorite folders (#7998)
closes - #5755 --------- Co-authored-by: martmull <martmull@hotmail.fr> Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -8,10 +8,12 @@ export const favoriteId = 'f088c8c9-05d2-4276-b065-b863cc7d0b33';
|
||||
const favoriteTargetObjectId = 'f2d8b9e9-7932-4065-bc09-baf12388b75d';
|
||||
export const favoriteTargetObjectRecord = {
|
||||
id: favoriteTargetObjectId,
|
||||
__typename: 'Person',
|
||||
};
|
||||
|
||||
export const initialFavorites = [
|
||||
{
|
||||
__typename: 'Favorite',
|
||||
id: '1',
|
||||
position: 0,
|
||||
key: mockId,
|
||||
@ -22,8 +24,11 @@ export const initialFavorites = [
|
||||
recordId: '1',
|
||||
person: { id: '1', name: 'John Doe' },
|
||||
company: { id: '2', name: 'ABC Corp' },
|
||||
workspaceMemberId: '1',
|
||||
favoriteFolderId: undefined,
|
||||
},
|
||||
{
|
||||
__typename: 'Favorite',
|
||||
id: '2',
|
||||
position: 1,
|
||||
key: mockId,
|
||||
@ -34,8 +39,12 @@ export const initialFavorites = [
|
||||
recordId: '1',
|
||||
person: { id: '3', name: 'Jane Doe' },
|
||||
company: { id: '4', name: 'Company Test' },
|
||||
workspaceMemberId: '1',
|
||||
favoriteFolderId: undefined,
|
||||
|
||||
},
|
||||
{
|
||||
__typename: 'Favorite',
|
||||
id: '3',
|
||||
position: 2,
|
||||
key: mockId,
|
||||
@ -44,27 +53,37 @@ export const initialFavorites = [
|
||||
avatarType: 'squared' as AvatarType,
|
||||
link: 'example.com',
|
||||
recordId: '1',
|
||||
workspaceMemberId: '1',
|
||||
favoriteFolderId: undefined,
|
||||
},
|
||||
];
|
||||
|
||||
export const sortedFavorites = [
|
||||
{
|
||||
id: '1',
|
||||
recordId: '2',
|
||||
recordId: '1',
|
||||
position: 0,
|
||||
avatarType: 'squared',
|
||||
avatarUrl: undefined,
|
||||
labelIdentifier: 'ABC Corp',
|
||||
link: '/object/company/2',
|
||||
avatarType: 'rounded',
|
||||
avatarUrl: '',
|
||||
labelIdentifier: ' ',
|
||||
link: '/object/person/1',
|
||||
objectNameSingular: 'person',
|
||||
workspaceMemberId: '1',
|
||||
favoriteFolderId: undefined,
|
||||
__typename: 'Favorite',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
recordId: '4',
|
||||
recordId: '3',
|
||||
position: 1,
|
||||
avatarType: 'squared',
|
||||
avatarUrl: undefined,
|
||||
labelIdentifier: 'Company Test',
|
||||
link: '/object/company/4',
|
||||
avatarType: 'rounded',
|
||||
avatarUrl: '',
|
||||
labelIdentifier: ' ',
|
||||
link: '/object/person/3',
|
||||
objectNameSingular: 'person',
|
||||
workspaceMemberId: '1',
|
||||
favoriteFolderId: undefined,
|
||||
__typename: 'Favorite',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
@ -72,9 +91,12 @@ export const sortedFavorites = [
|
||||
key: '8f3b2121-f194-4ba4-9fbf-2d5a37126806',
|
||||
labelIdentifier: 'favoriteLabel',
|
||||
avatarUrl: 'example.com',
|
||||
avatarType: 'squared',
|
||||
link: 'example.com',
|
||||
recordId: '1',
|
||||
avatarType: 'squared',
|
||||
favoriteFolderId: undefined,
|
||||
workspaceMemberId: '1',
|
||||
__typename: 'Favorite',
|
||||
},
|
||||
];
|
||||
|
||||
@ -84,288 +106,301 @@ export const mocks = [
|
||||
query: gql`
|
||||
mutation CreateOneFavorite($input: FavoriteCreateInput!) {
|
||||
createFavorite(data: $input) {
|
||||
__typename
|
||||
company {
|
||||
__typename
|
||||
accountOwnerId
|
||||
address {
|
||||
addressStreet1
|
||||
addressStreet2
|
||||
addressCity
|
||||
addressState
|
||||
addressCountry
|
||||
addressPostcode
|
||||
addressLat
|
||||
addressLng
|
||||
}
|
||||
annualRecurringRevenue {
|
||||
amountMicros
|
||||
currencyCode
|
||||
}
|
||||
createdAt
|
||||
createdBy {
|
||||
source
|
||||
__typename
|
||||
company {
|
||||
__typename
|
||||
accountOwnerId
|
||||
address {
|
||||
addressStreet1
|
||||
addressStreet2
|
||||
addressCity
|
||||
addressState
|
||||
addressCountry
|
||||
addressPostcode
|
||||
addressLat
|
||||
addressLng
|
||||
}
|
||||
annualRecurringRevenue {
|
||||
amountMicros
|
||||
currencyCode
|
||||
}
|
||||
createdAt
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
deletedAt
|
||||
domainName {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
employees
|
||||
id
|
||||
idealCustomerProfile
|
||||
introVideo {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
linkedinLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
name
|
||||
position
|
||||
tagline
|
||||
updatedAt
|
||||
visaSponsorship
|
||||
workPolicy
|
||||
xLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
}
|
||||
companyId
|
||||
createdAt
|
||||
deletedAt
|
||||
favoriteFolder {
|
||||
__typename
|
||||
createdAt
|
||||
deletedAt
|
||||
id
|
||||
name
|
||||
position
|
||||
updatedAt
|
||||
}
|
||||
favoriteFolderId
|
||||
id
|
||||
note {
|
||||
__typename
|
||||
body
|
||||
createdAt
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
deletedAt
|
||||
id
|
||||
position
|
||||
title
|
||||
updatedAt
|
||||
}
|
||||
noteId
|
||||
opportunity {
|
||||
__typename
|
||||
amount {
|
||||
amountMicros
|
||||
currencyCode
|
||||
}
|
||||
closeDate
|
||||
companyId
|
||||
createdAt
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
deletedAt
|
||||
id
|
||||
name
|
||||
pointOfContactId
|
||||
position
|
||||
stage
|
||||
updatedAt
|
||||
}
|
||||
opportunityId
|
||||
person {
|
||||
__typename
|
||||
avatarUrl
|
||||
city
|
||||
companyId
|
||||
createdAt
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
deletedAt
|
||||
emails {
|
||||
primaryEmail
|
||||
additionalEmails
|
||||
}
|
||||
id
|
||||
intro
|
||||
jobTitle
|
||||
linkedinLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
performanceRating
|
||||
phones {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
additionalPhones
|
||||
}
|
||||
position
|
||||
updatedAt
|
||||
whatsapp {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
additionalPhones
|
||||
}
|
||||
workPreference
|
||||
xLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
}
|
||||
personId
|
||||
position
|
||||
rocket {
|
||||
__typename
|
||||
createdAt
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
deletedAt
|
||||
id
|
||||
name
|
||||
position
|
||||
updatedAt
|
||||
}
|
||||
rocketId
|
||||
task {
|
||||
__typename
|
||||
assigneeId
|
||||
body
|
||||
createdAt
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
deletedAt
|
||||
dueAt
|
||||
id
|
||||
position
|
||||
status
|
||||
title
|
||||
updatedAt
|
||||
}
|
||||
taskId
|
||||
updatedAt
|
||||
view {
|
||||
__typename
|
||||
createdAt
|
||||
deletedAt
|
||||
icon
|
||||
id
|
||||
isCompact
|
||||
kanbanFieldMetadataId
|
||||
key
|
||||
name
|
||||
objectMetadataId
|
||||
position
|
||||
type
|
||||
updatedAt
|
||||
}
|
||||
viewId
|
||||
workflow {
|
||||
__typename
|
||||
createdAt
|
||||
deletedAt
|
||||
id
|
||||
lastPublishedVersionId
|
||||
name
|
||||
position
|
||||
statuses
|
||||
updatedAt
|
||||
}
|
||||
workflowId
|
||||
workflowRun {
|
||||
__typename
|
||||
createdAt
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
deletedAt
|
||||
endedAt
|
||||
id
|
||||
name
|
||||
output
|
||||
position
|
||||
startedAt
|
||||
status
|
||||
updatedAt
|
||||
workflowId
|
||||
workflowVersionId
|
||||
}
|
||||
workflowRunId
|
||||
workflowVersion {
|
||||
__typename
|
||||
createdAt
|
||||
deletedAt
|
||||
id
|
||||
name
|
||||
position
|
||||
status
|
||||
steps
|
||||
trigger
|
||||
updatedAt
|
||||
workflowId
|
||||
}
|
||||
workflowVersionId
|
||||
workspaceMember {
|
||||
__typename
|
||||
avatarUrl
|
||||
colorScheme
|
||||
createdAt
|
||||
dateFormat
|
||||
deletedAt
|
||||
id
|
||||
locale
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
timeFormat
|
||||
timeZone
|
||||
updatedAt
|
||||
userEmail
|
||||
userId
|
||||
}
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
deletedAt
|
||||
domainName {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
employees
|
||||
id
|
||||
idealCustomerProfile
|
||||
introVideo {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
linkedinLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
name
|
||||
position
|
||||
tagline
|
||||
updatedAt
|
||||
visaSponsorship
|
||||
workPolicy
|
||||
xLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
}
|
||||
companyId
|
||||
createdAt
|
||||
deletedAt
|
||||
id
|
||||
note {
|
||||
__typename
|
||||
body
|
||||
createdAt
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
deletedAt
|
||||
id
|
||||
position
|
||||
title
|
||||
updatedAt
|
||||
}
|
||||
noteId
|
||||
opportunity {
|
||||
__typename
|
||||
amount {
|
||||
amountMicros
|
||||
currencyCode
|
||||
}
|
||||
closeDate
|
||||
companyId
|
||||
createdAt
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
deletedAt
|
||||
id
|
||||
name
|
||||
pointOfContactId
|
||||
position
|
||||
stage
|
||||
updatedAt
|
||||
}
|
||||
opportunityId
|
||||
person {
|
||||
__typename
|
||||
avatarUrl
|
||||
city
|
||||
companyId
|
||||
createdAt
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
deletedAt
|
||||
emails {
|
||||
primaryEmail
|
||||
additionalEmails
|
||||
}
|
||||
id
|
||||
intro
|
||||
jobTitle
|
||||
linkedinLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
performanceRating
|
||||
phones {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
additionalPhones
|
||||
}
|
||||
position
|
||||
updatedAt
|
||||
whatsapp {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
additionalPhones
|
||||
}
|
||||
workPreference
|
||||
xLink {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
}
|
||||
personId
|
||||
position
|
||||
rocket {
|
||||
__typename
|
||||
createdAt
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
deletedAt
|
||||
id
|
||||
name
|
||||
position
|
||||
updatedAt
|
||||
}
|
||||
rocketId
|
||||
task {
|
||||
__typename
|
||||
assigneeId
|
||||
body
|
||||
createdAt
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
deletedAt
|
||||
dueAt
|
||||
id
|
||||
position
|
||||
status
|
||||
title
|
||||
updatedAt
|
||||
}
|
||||
taskId
|
||||
updatedAt
|
||||
view {
|
||||
__typename
|
||||
createdAt
|
||||
deletedAt
|
||||
icon
|
||||
id
|
||||
isCompact
|
||||
kanbanFieldMetadataId
|
||||
key
|
||||
name
|
||||
objectMetadataId
|
||||
position
|
||||
type
|
||||
updatedAt
|
||||
}
|
||||
viewId
|
||||
workflow {
|
||||
__typename
|
||||
createdAt
|
||||
deletedAt
|
||||
id
|
||||
lastPublishedVersionId
|
||||
name
|
||||
position
|
||||
statuses
|
||||
updatedAt
|
||||
}
|
||||
workflowId
|
||||
workflowRun {
|
||||
__typename
|
||||
createdAt
|
||||
createdBy {
|
||||
source
|
||||
workspaceMemberId
|
||||
name
|
||||
}
|
||||
deletedAt
|
||||
endedAt
|
||||
id
|
||||
name
|
||||
output
|
||||
position
|
||||
startedAt
|
||||
status
|
||||
updatedAt
|
||||
workflowId
|
||||
workflowVersionId
|
||||
}
|
||||
workflowRunId
|
||||
workflowVersion {
|
||||
__typename
|
||||
createdAt
|
||||
deletedAt
|
||||
id
|
||||
name
|
||||
position
|
||||
status
|
||||
steps
|
||||
trigger
|
||||
updatedAt
|
||||
workflowId
|
||||
}
|
||||
workflowVersionId
|
||||
workspaceMember {
|
||||
__typename
|
||||
avatarUrl
|
||||
colorScheme
|
||||
createdAt
|
||||
dateFormat
|
||||
deletedAt
|
||||
id
|
||||
locale
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
timeFormat
|
||||
timeZone
|
||||
updatedAt
|
||||
userEmail
|
||||
userId
|
||||
}
|
||||
workspaceMemberId
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
input: {
|
||||
id: mockId,
|
||||
personId: favoriteTargetObjectId,
|
||||
position: 4,
|
||||
position: 3,
|
||||
workspaceMemberId: '1',
|
||||
favoriteFolderId: undefined,
|
||||
id: mockId,
|
||||
},
|
||||
},
|
||||
},
|
||||
result: jest.fn(() => ({
|
||||
data: {
|
||||
createFavorite: {
|
||||
__typename: 'Favorite',
|
||||
id: favoriteId,
|
||||
position: 1,
|
||||
},
|
||||
},
|
||||
})),
|
||||
@ -386,7 +421,9 @@ export const mocks = [
|
||||
result: jest.fn(() => ({
|
||||
data: {
|
||||
deleteFavorite: {
|
||||
__typename: 'Favorite',
|
||||
id: favoriteId,
|
||||
deletedAt: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
})),
|
||||
@ -457,6 +494,16 @@ export const mocks = [
|
||||
companyId
|
||||
createdAt
|
||||
deletedAt
|
||||
favoriteFolder {
|
||||
__typename
|
||||
createdAt
|
||||
deletedAt
|
||||
id
|
||||
name
|
||||
position
|
||||
updatedAt
|
||||
}
|
||||
favoriteFolderId
|
||||
id
|
||||
note {
|
||||
__typename
|
||||
@ -678,7 +725,9 @@ export const mocks = [
|
||||
result: jest.fn(() => ({
|
||||
data: {
|
||||
updateFavorite: {
|
||||
__typename: 'Favorite',
|
||||
id: favoriteId,
|
||||
position: 2,
|
||||
},
|
||||
},
|
||||
})),
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { useCreateFavorite } from '@/favorites/hooks/useCreateFavorite';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import {
|
||||
favoriteTargetObjectRecord,
|
||||
initialFavorites,
|
||||
mockId,
|
||||
mockWorkspaceMember,
|
||||
mocks,
|
||||
} from '../__mocks__/useFavorites';
|
||||
|
||||
jest.mock('uuid', () => ({
|
||||
v4: () => mockId,
|
||||
}));
|
||||
|
||||
jest.mock('@/object-record/hooks/useFindManyRecords', () => ({
|
||||
useFindManyRecords: () => ({ records: initialFavorites }),
|
||||
}));
|
||||
|
||||
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
apolloMocks: mocks,
|
||||
});
|
||||
|
||||
describe('useCreateFavorite', () => {
|
||||
it('should create favorite successfully', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
setCurrentWorkspaceMember(mockWorkspaceMember);
|
||||
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
return useCreateFavorite();
|
||||
},
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
|
||||
result.current(favoriteTargetObjectRecord, CoreObjectNameSingular.Person);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mocks[0].result).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,47 @@
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import {
|
||||
favoriteId,
|
||||
initialFavorites,
|
||||
mockWorkspaceMember,
|
||||
mocks,
|
||||
} from '../__mocks__/useFavorites';
|
||||
|
||||
jest.mock('@/object-record/hooks/useFindManyRecords', () => ({
|
||||
useFindManyRecords: () => ({ records: initialFavorites }),
|
||||
}));
|
||||
|
||||
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
apolloMocks: mocks,
|
||||
});
|
||||
|
||||
describe('useDeleteFavorite', () => {
|
||||
it('should delete favorite successfully', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
setCurrentWorkspaceMember(mockWorkspaceMember);
|
||||
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
return useDeleteFavorite();
|
||||
},
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
|
||||
result.current(favoriteId);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mocks[1].result).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,29 +1,18 @@
|
||||
import { DropResult, ResponderProvided } from '@hello-pangea/dnd';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
|
||||
import { act } from 'react';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import {
|
||||
favoriteId,
|
||||
favoriteTargetObjectRecord,
|
||||
initialFavorites,
|
||||
mockId,
|
||||
mocks,
|
||||
mockWorkspaceMember,
|
||||
sortedFavorites,
|
||||
} from '../__mocks__/useFavorites';
|
||||
|
||||
jest.mock('uuid', () => ({
|
||||
v4: jest.fn(() => mockId),
|
||||
}));
|
||||
|
||||
jest.mock('@/object-record/hooks/useFindManyRecords', () => ({
|
||||
useFindManyRecords: () => ({ records: initialFavorites }),
|
||||
}));
|
||||
@ -33,7 +22,7 @@ const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
});
|
||||
|
||||
describe('useFavorites', () => {
|
||||
it('should fetch favorites successfully', async () => {
|
||||
it('should fetch and sort favorites successfully', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
@ -46,108 +35,9 @@ describe('useFavorites', () => {
|
||||
|
||||
return useFavorites();
|
||||
},
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
|
||||
expect(result.current.favorites).toEqual(sortedFavorites);
|
||||
});
|
||||
|
||||
it('should createOneFavorite successfully', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
setCurrentWorkspaceMember(mockWorkspaceMember);
|
||||
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
return useFavorites();
|
||||
},
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
result.current.createFavorite(
|
||||
favoriteTargetObjectRecord,
|
||||
CoreObjectNameSingular.Person,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mocks[0].result).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should deleteOneRecord successfully', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
setCurrentWorkspaceMember(mockWorkspaceMember);
|
||||
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
return useFavorites();
|
||||
},
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
result.current.deleteFavorite(favoriteId);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mocks[1].result).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle reordering favorites successfully', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
setCurrentWorkspaceMember(mockWorkspaceMember);
|
||||
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
return useFavorites();
|
||||
},
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
act(() => {
|
||||
const dragAndDropResult: DropResult = {
|
||||
source: { index: 0, droppableId: 'droppableId' },
|
||||
destination: { index: 2, droppableId: 'droppableId' },
|
||||
combine: null,
|
||||
mode: 'FLUID',
|
||||
draggableId: 'draggableId',
|
||||
type: 'type',
|
||||
reason: 'DROP',
|
||||
};
|
||||
|
||||
const responderProvided: ResponderProvided = {
|
||||
announce: () => {},
|
||||
};
|
||||
|
||||
result.current.handleReorderFavorite(
|
||||
dragAndDropResult,
|
||||
responderProvided,
|
||||
);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mocks[2].result).toHaveBeenCalled();
|
||||
});
|
||||
expect(result.current).toEqual(sortedFavorites);
|
||||
});
|
||||
});
|
||||
|
||||
@ -0,0 +1,64 @@
|
||||
import { DropResult, ResponderProvided } from '@hello-pangea/dnd';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { useReorderFavorite } from '@/favorites/hooks/useReorderFavorite';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import {
|
||||
initialFavorites,
|
||||
mockWorkspaceMember,
|
||||
mocks,
|
||||
} from '../__mocks__/useFavorites';
|
||||
|
||||
jest.mock('@/object-record/hooks/useFindManyRecords', () => ({
|
||||
useFindManyRecords: () => ({ records: initialFavorites }),
|
||||
}));
|
||||
|
||||
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
apolloMocks: mocks,
|
||||
});
|
||||
|
||||
describe('useReorderFavorite', () => {
|
||||
it('should handle reordering favorites successfully', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
setCurrentWorkspaceMember(mockWorkspaceMember);
|
||||
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
return useReorderFavorite();
|
||||
},
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
const dragAndDropResult: DropResult = {
|
||||
source: { index: 0, droppableId: 'droppableId' },
|
||||
destination: { index: 2, droppableId: 'droppableId' },
|
||||
combine: null,
|
||||
mode: 'FLUID',
|
||||
draggableId: '1',
|
||||
type: 'type',
|
||||
reason: 'DROP',
|
||||
};
|
||||
|
||||
const responderProvided: ResponderProvided = {
|
||||
announce: () => {},
|
||||
};
|
||||
|
||||
result.current(dragAndDropResult, responderProvided);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mocks[2].result).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,38 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { usePrefetchedFavoritesData } from './usePrefetchedFavoritesData';
|
||||
|
||||
export const useCreateFavorite = () => {
|
||||
const { favorites, currentWorkspaceMemberId } = usePrefetchedFavoritesData();
|
||||
const { createOneRecord: createOneFavorite } = useCreateOneRecord({
|
||||
objectNameSingular: CoreObjectNameSingular.Favorite,
|
||||
});
|
||||
|
||||
const createFavorite = (
|
||||
targetRecord: ObjectRecord,
|
||||
targetObjectNameSingular: string,
|
||||
favoriteFolderId?: string,
|
||||
) => {
|
||||
const relevantFavorites = favoriteFolderId
|
||||
? favorites.filter((fav) => fav.favoriteFolderId === favoriteFolderId)
|
||||
: favorites.filter(
|
||||
(fav) => !fav.favoriteFolderId && fav.workspaceMemberId,
|
||||
);
|
||||
|
||||
const maxPosition = Math.max(
|
||||
...relevantFavorites.map((fav) => fav.position),
|
||||
0,
|
||||
);
|
||||
|
||||
createOneFavorite({
|
||||
[targetObjectNameSingular]: targetRecord,
|
||||
[`${targetObjectNameSingular}Id`]: targetRecord.id,
|
||||
position: maxPosition + 1,
|
||||
workspaceMemberId: currentWorkspaceMemberId,
|
||||
favoriteFolderId,
|
||||
});
|
||||
};
|
||||
|
||||
return createFavorite;
|
||||
};
|
||||
@ -0,0 +1,32 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { usePrefetchedFavoritesData } from './usePrefetchedFavoritesData';
|
||||
import { usePrefetchedFavoritesFoldersData } from './usePrefetchedFavoritesFoldersData';
|
||||
|
||||
export const useCreateFavoriteFolder = () => {
|
||||
const { createOneRecord: createFavoriteFolder } = useCreateOneRecord({
|
||||
objectNameSingular: CoreObjectNameSingular.FavoriteFolder,
|
||||
});
|
||||
|
||||
const { currentWorkspaceMemberId } = usePrefetchedFavoritesData();
|
||||
const { favoriteFolders } = usePrefetchedFavoritesFoldersData();
|
||||
|
||||
const createNewFavoriteFolder = async (name: string): Promise<void> => {
|
||||
if (!name || !currentWorkspaceMemberId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const maxPosition = Math.max(
|
||||
...favoriteFolders.map((folder) => folder.position),
|
||||
0,
|
||||
);
|
||||
|
||||
await createFavoriteFolder({
|
||||
workspaceMemberId: currentWorkspaceMemberId,
|
||||
name,
|
||||
position: maxPosition + 1,
|
||||
});
|
||||
};
|
||||
|
||||
return createNewFavoriteFolder;
|
||||
};
|
||||
@ -0,0 +1,14 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||
|
||||
export const useDeleteFavorite = () => {
|
||||
const { deleteOneRecord } = useDeleteOneRecord({
|
||||
objectNameSingular: CoreObjectNameSingular.Favorite,
|
||||
});
|
||||
|
||||
const deleteFavorite = (favoriteId: string) => {
|
||||
deleteOneRecord(favoriteId);
|
||||
};
|
||||
|
||||
return deleteFavorite;
|
||||
};
|
||||
@ -0,0 +1,26 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||
import { usePrefetchedFavoritesData } from './usePrefetchedFavoritesData';
|
||||
|
||||
export const useDeleteFavoriteFolder = () => {
|
||||
const { deleteOneRecord } = useDeleteOneRecord({
|
||||
objectNameSingular: CoreObjectNameSingular.FavoriteFolder,
|
||||
});
|
||||
const { upsertFavorites, favorites, workspaceFavorites } =
|
||||
usePrefetchedFavoritesData();
|
||||
|
||||
const deleteFavoriteFolder = async (folderId: string): Promise<void> => {
|
||||
await deleteOneRecord(folderId);
|
||||
|
||||
const updatedFavorites = [
|
||||
...favorites.filter((favorite) => favorite.favoriteFolderId !== folderId),
|
||||
...workspaceFavorites,
|
||||
];
|
||||
|
||||
upsertFavorites(updatedFavorites);
|
||||
};
|
||||
|
||||
return {
|
||||
deleteFavoriteFolder,
|
||||
};
|
||||
};
|
||||
@ -1,163 +1,56 @@
|
||||
import { OnDragEndResponder } from '@hello-pangea/dnd';
|
||||
import { useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { Favorite } from '@/favorites/types/Favorite';
|
||||
import { sortFavorites } from '@/favorites/utils/sortFavorites';
|
||||
import { useGetObjectRecordIdentifierByNameSingular } from '@/object-metadata/hooks/useGetObjectRecordIdentifierByNameSingular';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||
import { View } from '@/views/types/View';
|
||||
import { useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { usePrefetchedFavoritesData } from './usePrefetchedFavoritesData';
|
||||
|
||||
export const useFavorites = () => {
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
|
||||
const { favorites } = usePrefetchedFavoritesData();
|
||||
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews);
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
const { objectMetadataItem: favoriteObjectMetadataItem } =
|
||||
useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.Favorite,
|
||||
});
|
||||
|
||||
const { deleteOneRecord } = useDeleteOneRecord({
|
||||
objectNameSingular: CoreObjectNameSingular.Favorite,
|
||||
});
|
||||
|
||||
const { updateOneRecord: updateOneFavorite } = useUpdateOneRecord({
|
||||
objectNameSingular: CoreObjectNameSingular.Favorite,
|
||||
});
|
||||
|
||||
const { createOneRecord: createOneFavorite } = useCreateOneRecord({
|
||||
objectNameSingular: CoreObjectNameSingular.Favorite,
|
||||
});
|
||||
|
||||
const { records: favorites } = usePrefetchedData<Favorite>(
|
||||
PrefetchKey.AllFavorites,
|
||||
{
|
||||
workspaceMemberId: {
|
||||
eq: currentWorkspaceMember?.id ?? '',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const { records: workspaceFavorites } = usePrefetchedData<Favorite>(
|
||||
PrefetchKey.AllFavorites,
|
||||
{
|
||||
workspaceMemberId: {
|
||||
eq: undefined,
|
||||
},
|
||||
},
|
||||
);
|
||||
const getObjectRecordIdentifierByNameSingular =
|
||||
useGetObjectRecordIdentifierByNameSingular();
|
||||
|
||||
const favoriteRelationFieldMetadataItems = useMemo(
|
||||
() =>
|
||||
favoriteObjectMetadataItem.fields.filter(
|
||||
(fieldMetadataItem) =>
|
||||
fieldMetadataItem.type === FieldMetadataType.Relation &&
|
||||
fieldMetadataItem.name !== 'workspaceMember',
|
||||
fieldMetadataItem.name !== 'workspaceMember' &&
|
||||
fieldMetadataItem.name !== 'favoriteFolder',
|
||||
),
|
||||
[favoriteObjectMetadataItem.fields],
|
||||
);
|
||||
|
||||
const getObjectRecordIdentifierByNameSingular =
|
||||
useGetObjectRecordIdentifierByNameSingular();
|
||||
|
||||
const favoritesSorted = useMemo(() => {
|
||||
return sortFavorites(
|
||||
const sortedFavorites = useMemo(
|
||||
() =>
|
||||
sortFavorites(
|
||||
favorites,
|
||||
favoriteRelationFieldMetadataItems,
|
||||
getObjectRecordIdentifierByNameSingular,
|
||||
true,
|
||||
views,
|
||||
objectMetadataItems,
|
||||
),
|
||||
[
|
||||
favorites,
|
||||
favoriteRelationFieldMetadataItems,
|
||||
getObjectRecordIdentifierByNameSingular,
|
||||
true,
|
||||
);
|
||||
}, [
|
||||
favoriteRelationFieldMetadataItems,
|
||||
favorites,
|
||||
getObjectRecordIdentifierByNameSingular,
|
||||
]);
|
||||
views,
|
||||
objectMetadataItems,
|
||||
],
|
||||
);
|
||||
|
||||
const workspaceFavoritesSorted = useMemo(() => {
|
||||
return sortFavorites(
|
||||
workspaceFavorites.filter((favorite) => favorite.viewId),
|
||||
favoriteRelationFieldMetadataItems,
|
||||
getObjectRecordIdentifierByNameSingular,
|
||||
false,
|
||||
);
|
||||
}, [
|
||||
favoriteRelationFieldMetadataItems,
|
||||
getObjectRecordIdentifierByNameSingular,
|
||||
workspaceFavorites,
|
||||
]);
|
||||
|
||||
const createFavorite = (
|
||||
targetRecord: Record<string, any>,
|
||||
targetObjectNameSingular: string,
|
||||
) => {
|
||||
createOneFavorite({
|
||||
[targetObjectNameSingular]: targetRecord,
|
||||
[`${targetObjectNameSingular}Id`]: targetRecord.id,
|
||||
position: favorites.length + 1,
|
||||
workspaceMemberId: currentWorkspaceMember?.id,
|
||||
});
|
||||
};
|
||||
|
||||
const deleteFavorite = (favoriteId: string) => {
|
||||
deleteOneRecord(favoriteId);
|
||||
};
|
||||
|
||||
const computeNewPosition = (destIndex: number, sourceIndex: number) => {
|
||||
const moveToFirstPosition = destIndex === 0;
|
||||
const moveToLastPosition = destIndex === favoritesSorted.length - 1;
|
||||
const moveAfterSource = destIndex > sourceIndex;
|
||||
|
||||
if (moveToFirstPosition) {
|
||||
return favoritesSorted[0].position / 2;
|
||||
} else if (moveToLastPosition) {
|
||||
return favoritesSorted[destIndex - 1].position + 1;
|
||||
} else if (moveAfterSource) {
|
||||
return (
|
||||
(favoritesSorted[destIndex + 1].position +
|
||||
favoritesSorted[destIndex].position) /
|
||||
2
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
favoritesSorted[destIndex].position -
|
||||
(favoritesSorted[destIndex].position -
|
||||
favoritesSorted[destIndex - 1].position) /
|
||||
2
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleReorderFavorite: OnDragEndResponder = (result) => {
|
||||
if (!result.destination || !favoritesSorted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newPosition = computeNewPosition(
|
||||
result.destination.index,
|
||||
result.source.index,
|
||||
);
|
||||
|
||||
const updatedFavorite = favoritesSorted[result.source.index];
|
||||
|
||||
updateOneFavorite({
|
||||
idToUpdate: updatedFavorite.id,
|
||||
updateOneRecordInput: {
|
||||
position: newPosition,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
favorites: favoritesSorted,
|
||||
workspaceFavorites: workspaceFavoritesSorted,
|
||||
createFavorite,
|
||||
handleReorderFavorite,
|
||||
deleteFavorite,
|
||||
};
|
||||
return sortedFavorites;
|
||||
};
|
||||
|
||||
@ -0,0 +1,62 @@
|
||||
import { sortFavorites } from '@/favorites/utils/sortFavorites';
|
||||
import { useGetObjectRecordIdentifierByNameSingular } from '@/object-metadata/hooks/useGetObjectRecordIdentifierByNameSingular';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||
import { View } from '@/views/types/View';
|
||||
import { useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { usePrefetchedFavoritesData } from './usePrefetchedFavoritesData';
|
||||
import { usePrefetchedFavoritesFoldersData } from './usePrefetchedFavoritesFoldersData';
|
||||
|
||||
export const useFavoritesByFolder = () => {
|
||||
const { favorites } = usePrefetchedFavoritesData();
|
||||
const { favoriteFolders } = usePrefetchedFavoritesFoldersData();
|
||||
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews);
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
const getObjectRecordIdentifierByNameSingular =
|
||||
useGetObjectRecordIdentifierByNameSingular();
|
||||
|
||||
const { objectMetadataItem: favoriteObjectMetadataItem } =
|
||||
useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.Favorite,
|
||||
});
|
||||
|
||||
const favoriteRelationFields = useMemo(
|
||||
() =>
|
||||
favoriteObjectMetadataItem.fields.filter(
|
||||
(fieldMetadataItem) =>
|
||||
fieldMetadataItem.type === FieldMetadataType.Relation &&
|
||||
fieldMetadataItem.name !== 'workspaceMember' &&
|
||||
fieldMetadataItem.name !== 'favoriteFolder',
|
||||
),
|
||||
[favoriteObjectMetadataItem.fields],
|
||||
);
|
||||
|
||||
const favoritesByFolder = useMemo(() => {
|
||||
return favoriteFolders.map((folder) => ({
|
||||
folderId: folder.id,
|
||||
folderName: folder.name,
|
||||
favorites: sortFavorites(
|
||||
favorites.filter((favorite) => favorite.favoriteFolderId === folder.id),
|
||||
favoriteRelationFields,
|
||||
getObjectRecordIdentifierByNameSingular,
|
||||
true,
|
||||
views,
|
||||
objectMetadataItems,
|
||||
),
|
||||
}));
|
||||
}, [
|
||||
favoriteFolders,
|
||||
favorites,
|
||||
favoriteRelationFields,
|
||||
getObjectRecordIdentifierByNameSingular,
|
||||
views,
|
||||
objectMetadataItems,
|
||||
]);
|
||||
|
||||
return favoritesByFolder;
|
||||
};
|
||||
@ -0,0 +1,46 @@
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { Favorite } from '@/favorites/types/Favorite';
|
||||
import { usePrefetchRunQuery } from '@/prefetch/hooks/internal/usePrefetchRunQuery';
|
||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
type PrefetchedFavoritesData = {
|
||||
favorites: Favorite[];
|
||||
workspaceFavorites: Favorite[];
|
||||
upsertFavorites: (records: Favorite[]) => void;
|
||||
currentWorkspaceMemberId: string | undefined;
|
||||
};
|
||||
|
||||
export const usePrefetchedFavoritesData = (): PrefetchedFavoritesData => {
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const currentWorkspaceMemberId = currentWorkspaceMember?.id;
|
||||
const { records: _favorites } = usePrefetchedData<Favorite>(
|
||||
PrefetchKey.AllFavorites,
|
||||
{
|
||||
workspaceMemberId: {
|
||||
eq: currentWorkspaceMemberId,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const favorites = _favorites.filter(
|
||||
(favorite) => favorite.workspaceMemberId === currentWorkspaceMemberId,
|
||||
);
|
||||
|
||||
const workspaceFavorites = _favorites.filter(
|
||||
(favorite) => favorite.workspaceMemberId === null,
|
||||
);
|
||||
|
||||
const { upsertRecordsInCache: upsertFavorites } =
|
||||
usePrefetchRunQuery<Favorite>({
|
||||
prefetchKey: PrefetchKey.AllFavorites,
|
||||
});
|
||||
|
||||
return {
|
||||
favorites,
|
||||
workspaceFavorites,
|
||||
upsertFavorites,
|
||||
currentWorkspaceMemberId,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,38 @@
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { FavoriteFolder } from '@/favorites/types/FavoriteFolder';
|
||||
import { usePrefetchRunQuery } from '@/prefetch/hooks/internal/usePrefetchRunQuery';
|
||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
type PrefetchedFavoritesFoldersData = {
|
||||
favoriteFolders: FavoriteFolder[];
|
||||
upsertFavoriteFolders: (records: FavoriteFolder[]) => void;
|
||||
currentWorkspaceMemberId: string | undefined;
|
||||
};
|
||||
|
||||
export const usePrefetchedFavoritesFoldersData =
|
||||
(): PrefetchedFavoritesFoldersData => {
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const currentWorkspaceMemberId = currentWorkspaceMember?.id;
|
||||
|
||||
const { records: favoriteFolders } = usePrefetchedData<FavoriteFolder>(
|
||||
PrefetchKey.AllFavoritesFolders,
|
||||
{
|
||||
workspaceMemberId: {
|
||||
eq: currentWorkspaceMemberId,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const { upsertRecordsInCache: upsertFavoriteFolders } =
|
||||
usePrefetchRunQuery<FavoriteFolder>({
|
||||
prefetchKey: PrefetchKey.AllFavoritesFolders,
|
||||
});
|
||||
|
||||
return {
|
||||
favoriteFolders,
|
||||
upsertFavoriteFolders,
|
||||
currentWorkspaceMemberId,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,28 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
|
||||
export const useRenameFavoriteFolder = () => {
|
||||
const { updateOneRecord: updateFavoriteFolder } = useUpdateOneRecord({
|
||||
objectNameSingular: CoreObjectNameSingular.FavoriteFolder,
|
||||
});
|
||||
|
||||
const renameFavoriteFolder = async (
|
||||
folderId: string,
|
||||
newName: string,
|
||||
): Promise<void> => {
|
||||
if (!newName) {
|
||||
return;
|
||||
}
|
||||
|
||||
await updateFavoriteFolder({
|
||||
idToUpdate: folderId,
|
||||
updateOneRecordInput: {
|
||||
name: newName,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
renameFavoriteFolder,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,41 @@
|
||||
import { useSortedFavorites } from '@/favorites/hooks/useSortedFavorites';
|
||||
import { calculateNewPosition } from '@/favorites/utils/calculateNewPosition';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { OnDragEndResponder } from '@hello-pangea/dnd';
|
||||
import { usePrefetchedFavoritesData } from './usePrefetchedFavoritesData';
|
||||
|
||||
export const useReorderFavorite = () => {
|
||||
const { favorites } = usePrefetchedFavoritesData();
|
||||
const { favoritesSorted } = useSortedFavorites();
|
||||
const { updateOneRecord: updateOneFavorite } = useUpdateOneRecord({
|
||||
objectNameSingular: CoreObjectNameSingular.Favorite,
|
||||
});
|
||||
|
||||
const reorderFavorite: OnDragEndResponder = (result) => {
|
||||
if (!result.destination) return;
|
||||
|
||||
const draggedFavoriteId = result.draggableId;
|
||||
const draggedFavorite = favorites.find((f) => f.id === draggedFavoriteId);
|
||||
|
||||
if (!draggedFavorite) return;
|
||||
|
||||
const inSameFolderFavorites = favoritesSorted.filter(
|
||||
(fav) => fav.favoriteFolderId === draggedFavorite.favoriteFolderId,
|
||||
);
|
||||
if (!inSameFolderFavorites.length) return;
|
||||
|
||||
const newPosition = calculateNewPosition({
|
||||
destinationIndex: result.destination.index,
|
||||
sourceIndex: result.source.index,
|
||||
items: inSameFolderFavorites,
|
||||
});
|
||||
|
||||
updateOneFavorite({
|
||||
idToUpdate: draggedFavoriteId,
|
||||
updateOneRecordInput: { position: newPosition },
|
||||
});
|
||||
};
|
||||
|
||||
return reorderFavorite;
|
||||
};
|
||||
@ -0,0 +1,75 @@
|
||||
import { sortFavorites } from '@/favorites/utils/sortFavorites';
|
||||
import { useGetObjectRecordIdentifierByNameSingular } from '@/object-metadata/hooks/useGetObjectRecordIdentifierByNameSingular';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||
import { View } from '@/views/types/View';
|
||||
import { useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { usePrefetchedFavoritesData } from './usePrefetchedFavoritesData';
|
||||
|
||||
export const useSortedFavorites = () => {
|
||||
const { favorites, workspaceFavorites } = usePrefetchedFavoritesData();
|
||||
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews);
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
const { objectMetadataItem: favoriteObjectMetadataItem } =
|
||||
useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.Favorite,
|
||||
});
|
||||
|
||||
const getObjectRecordIdentifierByNameSingular =
|
||||
useGetObjectRecordIdentifierByNameSingular();
|
||||
|
||||
const favoriteRelationFieldMetadataItems = useMemo(
|
||||
() =>
|
||||
favoriteObjectMetadataItem.fields.filter(
|
||||
(fieldMetadataItem) =>
|
||||
fieldMetadataItem.type === FieldMetadataType.Relation &&
|
||||
fieldMetadataItem.name !== 'workspaceMember' &&
|
||||
fieldMetadataItem.name !== 'favoriteFolder',
|
||||
),
|
||||
[favoriteObjectMetadataItem.fields],
|
||||
);
|
||||
|
||||
const favoritesSorted = useMemo(() => {
|
||||
return sortFavorites(
|
||||
favorites,
|
||||
favoriteRelationFieldMetadataItems,
|
||||
getObjectRecordIdentifierByNameSingular,
|
||||
true,
|
||||
views,
|
||||
objectMetadataItems,
|
||||
);
|
||||
}, [
|
||||
favoriteRelationFieldMetadataItems,
|
||||
favorites,
|
||||
getObjectRecordIdentifierByNameSingular,
|
||||
views,
|
||||
objectMetadataItems,
|
||||
]);
|
||||
|
||||
const workspaceFavoritesSorted = useMemo(() => {
|
||||
return sortFavorites(
|
||||
workspaceFavorites.filter((favorite) => favorite.viewId),
|
||||
favoriteRelationFieldMetadataItems,
|
||||
getObjectRecordIdentifierByNameSingular,
|
||||
false,
|
||||
views,
|
||||
objectMetadataItems,
|
||||
);
|
||||
}, [
|
||||
favoriteRelationFieldMetadataItems,
|
||||
getObjectRecordIdentifierByNameSingular,
|
||||
workspaceFavorites,
|
||||
views,
|
||||
objectMetadataItems,
|
||||
]);
|
||||
|
||||
return {
|
||||
favoritesSorted,
|
||||
workspaceFavoritesSorted,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,56 @@
|
||||
import { sortFavorites } from '@/favorites/utils/sortFavorites';
|
||||
import { useGetObjectRecordIdentifierByNameSingular } from '@/object-metadata/hooks/useGetObjectRecordIdentifierByNameSingular';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||
import { View } from '@/views/types/View';
|
||||
import { useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { usePrefetchedFavoritesData } from './usePrefetchedFavoritesData';
|
||||
|
||||
export const useWorkspaceFavorites = () => {
|
||||
const { workspaceFavorites } = usePrefetchedFavoritesData();
|
||||
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews);
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
const { objectMetadataItem: favoriteObjectMetadataItem } =
|
||||
useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.Favorite,
|
||||
});
|
||||
const getObjectRecordIdentifierByNameSingular =
|
||||
useGetObjectRecordIdentifierByNameSingular();
|
||||
|
||||
const favoriteRelationFieldMetadataItems = useMemo(
|
||||
() =>
|
||||
favoriteObjectMetadataItem.fields.filter(
|
||||
(fieldMetadataItem) =>
|
||||
fieldMetadataItem.type === FieldMetadataType.Relation &&
|
||||
fieldMetadataItem.name !== 'workspaceMember' &&
|
||||
fieldMetadataItem.name !== 'favoriteFolder',
|
||||
),
|
||||
[favoriteObjectMetadataItem.fields],
|
||||
);
|
||||
|
||||
const sortedWorkspaceFavorites = useMemo(
|
||||
() =>
|
||||
sortFavorites(
|
||||
workspaceFavorites.filter((favorite) => favorite.viewId),
|
||||
favoriteRelationFieldMetadataItems,
|
||||
getObjectRecordIdentifierByNameSingular,
|
||||
false,
|
||||
views,
|
||||
objectMetadataItems,
|
||||
),
|
||||
[
|
||||
workspaceFavorites,
|
||||
favoriteRelationFieldMetadataItems,
|
||||
getObjectRecordIdentifierByNameSingular,
|
||||
views,
|
||||
objectMetadataItems,
|
||||
],
|
||||
);
|
||||
|
||||
return sortedWorkspaceFavorites;
|
||||
};
|
||||
Reference in New Issue
Block a user