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:
@ -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);
|
||||
});
|
||||
});
|
||||
@ -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
|
||||
);
|
||||
};
|
||||
@ -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,
|
||||
};
|
||||
};
|
||||
@ -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;
|
||||
};
|
||||
@ -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);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user