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:
nitin
2024-11-18 19:52:19 +05:30
committed by GitHub
parent 5115022355
commit 0125d58ba8
100 changed files with 24033 additions and 21488 deletions

View File

@ -0,0 +1,55 @@
import { isLocationMatchingFavorite } from '../isLocationMatchingFavorite';
describe('isLocationMatchingFavorite', () => {
it('should return true if favorite link matches current path', () => {
const currentPath = '/app/objects/people';
const currentViewPath = '/app/objects/people?view=123';
const favorite = {
objectNameSingular: 'object',
link: '/app/objects/people',
};
expect(
isLocationMatchingFavorite(currentPath, currentViewPath, favorite),
).toBe(true);
});
it('should return true if favorite link matches current view path', () => {
const currentPath = '/app/object/company/12';
const currentViewPath = '/app/object/company/12?view=123';
const favorite = {
objectNameSingular: 'company',
link: '/app/object/company/12',
};
expect(
isLocationMatchingFavorite(currentPath, currentViewPath, favorite),
).toBe(true);
});
it('should return false if favorite link does not match current path', () => {
const currentPath = '/app/objects/people';
const currentViewPath = '/app/objects/people?view=123';
const favorite = {
objectNameSingular: 'object',
link: '/app/objects/company',
};
expect(
isLocationMatchingFavorite(currentPath, currentViewPath, favorite),
).toBe(false);
});
it('should return false if favorite link does not match current view path', () => {
const currentPath = '/app/objects/companies';
const currentViewPath = '/app/objects/companies?view=123';
const favorite = {
objectNameSingular: 'view',
link: '/app/objects/companies/view=246',
};
expect(
isLocationMatchingFavorite(currentPath, currentViewPath, favorite),
).toBe(false);
});
});

View File

@ -0,0 +1,33 @@
type CalculateNewPositionParams = {
destinationIndex: number;
sourceIndex: number;
items: Array<{ position: number }>;
};
export const calculateNewPosition = ({
destinationIndex,
sourceIndex,
items,
}: CalculateNewPositionParams): number => {
if (destinationIndex === 0) {
return items[0].position / 2;
}
if (destinationIndex === items.length - 1) {
return items[destinationIndex - 1].position + 1;
}
if (destinationIndex > sourceIndex) {
return (
(items[destinationIndex + 1].position +
items[destinationIndex].position) /
2
);
}
return (
items[destinationIndex].position -
(items[destinationIndex].position - items[destinationIndex - 1].position) /
2
);
};

View File

@ -0,0 +1,40 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { View } from '@/views/types/View';
import { isDefined } from 'twenty-ui';
type ReturnType = {
labelPlural: string;
view: View | null;
};
export const getObjectMetadataLabelPluralFromViewId = (
views: View[],
objectMetadataItems: ObjectMetadataItem[],
viewId: string,
): ReturnType => {
const view = views.find((view) => view.id === viewId);
if (!view) {
return {
labelPlural: '',
view: null,
};
}
const objectMetadataItem = objectMetadataItems.find(
(objectMetadataItem) => objectMetadataItem.id === view.objectMetadataId,
);
if (!isDefined(objectMetadataItem)) {
throw new Error(
`Object metadata item not found for id ${view.objectMetadataId}`,
);
}
const { labelPlural } = objectMetadataItem;
return {
labelPlural,
view,
};
};

View File

@ -0,0 +1,12 @@
import { ProcessedFavorite } from '@/favorites/utils/sortFavorites';
// Todo: we could only path the fullPath here (which is currentViewPath) and then split it in the function
export const isLocationMatchingFavorite = (
currentPath: string,
currentViewPath: string,
favorite: Pick<ProcessedFavorite, 'objectNameSingular' | 'link'>,
) => {
return favorite.objectNameSingular === 'view'
? favorite.link === currentViewPath
: favorite.link === currentPath;
};

View File

@ -1,19 +1,53 @@
import { Favorite } from '@/favorites/types/Favorite';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ObjectRecordIdentifier } from '@/object-record/types/ObjectRecordIdentifier';
import { View } from '@/views/types/View';
import { isDefined } from 'twenty-ui';
import { getObjectMetadataLabelPluralFromViewId } from './getObjectMetadataLabelPluralFromViewId';
export type ProcessedFavorite = Favorite & {
Icon?: string;
objectNameSingular?: string;
};
export const sortFavorites = (
favorites: Favorite[],
favoriteRelationFieldMetadataItems: FieldMetadataItem[],
getObjectRecordIdentifierByNameSingular: (
record: any,
record: ObjectRecord,
objectNameSingular: string,
) => ObjectRecordIdentifier,
hasLinkToShowPage: boolean,
views: View[],
objectMetadataItems: ObjectMetadataItem[],
) => {
return favorites
.map((favorite) => {
if (isDefined(favorite.viewId) && isDefined(favorite.workspaceMemberId)) {
const { labelPlural, view } = getObjectMetadataLabelPluralFromViewId(
views,
objectMetadataItems,
favorite.viewId,
);
return {
__typename: 'Favorite',
id: favorite.id,
recordId: view?.id,
position: favorite.position,
avatarType: 'icon',
avatarUrl: '',
labelIdentifier: view?.name,
link: `/objects/${labelPlural.toLocaleLowerCase()}${favorite.viewId ? `?view=${favorite.viewId}` : ''}`,
workspaceMemberId: favorite.workspaceMemberId,
favoriteFolderId: favorite.favoriteFolderId,
objectNameSingular: 'view',
Icon: view?.icon,
} as ProcessedFavorite;
}
for (const relationField of favoriteRelationFieldMetadataItems) {
if (isDefined(favorite[relationField.name])) {
const relationObject = favorite[relationField.name];
@ -29,6 +63,7 @@ export const sortFavorites = (
);
return {
__typename: 'Favorite',
id: favorite.id,
recordId: objectRecordIdentifier.id,
position: favorite.position,
@ -39,11 +74,14 @@ export const sortFavorites = (
? objectRecordIdentifier.linkToShowPage
: '',
workspaceMemberId: favorite.workspaceMemberId,
} as Favorite;
favoriteFolderId: favorite.favoriteFolderId,
objectNameSingular: relationObjectNameSingular,
} as ProcessedFavorite;
}
}
return favorite;
return {
...favorite,
} as ProcessedFavorite;
})
.sort((a, b) => a.position - b.position);
};