1721/feature/drag and drop favorites (#2097)

* prisma schema updated: added index in favorite

* update abilitiy added for favorite

* update one favorite resolver added

* update on favorite mutation added

* updateFavoriteOrder added

* Draglist added in favorite

* nav item convert to div from button: because it was not working dragable with button

* changed index to position

* position added in getFavorites query

* added recoil state for favorites

* reordering updated according to new method

* Use accurate type

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Abhishek Thory
2023-10-19 16:35:23 -05:00
committed by GitHub
parent f6b5943fc6
commit 08772b4456
14 changed files with 341 additions and 42 deletions

View File

@ -1,11 +1,17 @@
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem';
import { DraggableList } from '@/ui/layout/draggable-list/components/DraggableList';
import NavItem from '@/ui/navigation/navbar/components/NavItem';
import NavTitle from '@/ui/navigation/navbar/components/NavTitle';
import { Avatar } from '@/users/components/Avatar';
import { useGetFavoritesQuery } from '~/generated/graphql';
import { getLogoUrlFromDomainName } from '~/utils';
import { useFavorites } from '../hooks/useFavorites';
import { favoritesState } from '../states/favoritesState';
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
@ -14,46 +20,94 @@ const StyledContainer = styled.div`
`;
export const Favorites = () => {
const { data } = useGetFavoritesQuery();
const favorites = data?.findFavorites;
const [favorites, setFavorites] = useRecoilState(favoritesState);
const { handleReorderFavorite } = useFavorites();
useGetFavoritesQuery({
onCompleted: (data) =>
setFavorites(
data?.findFavorites.map((favorite) => {
return {
id: favorite.id,
person: favorite.person
? {
id: favorite.person.id,
firstName: favorite.person.firstName,
lastName: favorite.person.lastName,
avatarUrl: favorite.person.avatarUrl,
}
: undefined,
company: favorite.company
? {
id: favorite.company.id,
name: favorite.company.name,
domainName: favorite.company.domainName,
}
: undefined,
position: favorite.position,
};
}) ?? [],
),
});
if (!favorites || favorites.length === 0) return <></>;
return (
<StyledContainer>
<NavTitle label="Favorites" />
{favorites.map(
({ id, person, company }) =>
(person && (
<NavItem
key={id}
label={`${person.firstName} ${person.lastName}`}
Icon={() => (
<Avatar
colorId={person.id}
avatarUrl={person.avatarUrl ?? ''}
type="rounded"
placeholder={`${person.firstName} ${person.lastName}`}
<DraggableList
onDragEnd={handleReorderFavorite}
draggableItems={
<>
{favorites.map((favorite, index) => {
const { id, person, company } = favorite;
return (
<DraggableItem
key={id}
draggableId={id}
index={index}
itemComponent={
<>
{person && (
<NavItem
key={id}
label={`${person.firstName} ${person.lastName}`}
Icon={() => (
<Avatar
colorId={person.id}
avatarUrl={person.avatarUrl ?? ''}
type="rounded"
placeholder={`${person.firstName} ${person.lastName}`}
/>
)}
to={`/person/${person.id}`}
/>
)}
{company && (
<NavItem
key={id}
label={company.name}
Icon={() => (
<Avatar
avatarUrl={
getLogoUrlFromDomainName(company.domainName) ??
''
}
type="squared"
placeholder={company.name}
/>
)}
to={`/companies/${company.id}`}
/>
)}
</>
}
/>
)}
to={`/person/${person.id}`}
/>
)) ??
(company && (
<NavItem
key={id}
label={company.name}
Icon={() => (
<Avatar
avatarUrl={getLogoUrlFromDomainName(company.domainName) ?? ''}
type="squared"
placeholder={company.name}
/>
)}
to={`/companies/${company.id}`}
/>
)),
)}
);
})}
</>
}
/>
</StyledContainer>
);
};

View File

@ -0,0 +1,28 @@
import { gql } from '@apollo/client';
export const UPDATE_FAVORITE = gql`
mutation UpdateOneFavorite(
$data: FavoriteUpdateInput!
$where: FavoriteWhereUniqueInput!
) {
updateOneFavorites(data: $data, where: $where) {
id
person {
id
firstName
lastName
avatarUrl
}
company {
id
name
domainName
accountOwner {
id
displayName
avatarUrl
}
}
}
}
`;

View File

@ -4,6 +4,7 @@ export const GET_FAVORITES = gql`
query GetFavorites {
findFavorites {
id
position
person {
id
firstName

View File

@ -1,25 +1,34 @@
import { getOperationName } from '@apollo/client/utilities';
import { OnDragEndResponder } from '@hello-pangea/dnd';
import { useRecoilState } from 'recoil';
import { GET_COMPANY } from '@/companies/graphql/queries/getCompany';
import { GET_PERSON } from '@/people/graphql/queries/getPerson';
import {
Favorite,
useDeleteFavoriteMutation,
useInsertCompanyFavoriteMutation,
useInsertPersonFavoriteMutation,
useUpdateOneFavoriteMutation,
} from '~/generated/graphql';
import { GET_FAVORITES } from '../graphql/queries/getFavorites';
import { favoritesState } from '../states/favoritesState';
export const useFavorites = () => {
const [favorites, setFavorites] = useRecoilState(favoritesState);
const [insertCompanyFavoriteMutation] = useInsertCompanyFavoriteMutation();
const [insertPersonFavoriteMutation] = useInsertPersonFavoriteMutation();
const [deleteFavoriteMutation] = useDeleteFavoriteMutation();
const [updateOneFavoritesMutation] = useUpdateOneFavoriteMutation();
const insertCompanyFavorite = (companyId: string) => {
insertCompanyFavoriteMutation({
variables: {
data: {
companyId,
position: favorites.length + 1,
},
},
refetchQueries: [
@ -34,6 +43,7 @@ export const useFavorites = () => {
variables: {
data: {
personId,
position: favorites.length + 1,
},
},
refetchQueries: [
@ -43,6 +53,25 @@ export const useFavorites = () => {
});
};
const updateFavoritePosition = async (
favorites: Pick<Favorite, 'id' | 'position'>,
) => {
await updateOneFavoritesMutation({
variables: {
data: {
position: favorites?.position,
},
where: {
id: favorites.id,
},
},
refetchQueries: [
getOperationName(GET_FAVORITES) ?? '',
getOperationName(GET_PERSON) ?? '',
getOperationName(GET_COMPANY) ?? '',
],
});
};
const deleteCompanyFavorite = (companyId: string) => {
deleteFavoriteMutation({
variables: {
@ -75,10 +104,37 @@ export const useFavorites = () => {
});
};
const computeNewPosition = (destIndex: number) => {
if (destIndex === 0) {
return favorites[destIndex].position / 2;
}
if (destIndex === favorites.length - 1) {
return favorites[destIndex].position + 1;
}
return (
(favorites[destIndex - 1].position + favorites[destIndex].position) / 2
);
};
const handleReorderFavorite: OnDragEndResponder = (result) => {
if (!result.destination || !favorites) {
return;
}
const newPosition = computeNewPosition(result.destination.index);
const reorderFavorites = Array.from(favorites);
const [removed] = reorderFavorites.splice(result.source.index, 1);
const removedFav = { ...removed, position: newPosition };
reorderFavorites.splice(result.destination.index, 0, removedFav);
setFavorites(reorderFavorites);
updateFavoritePosition(removedFav);
};
return {
insertCompanyFavorite,
insertPersonFavorite,
deleteCompanyFavorite,
deletePersonFavorite,
handleReorderFavorite,
};
};

View File

@ -0,0 +1,16 @@
import { atom } from 'recoil';
import { Company, Favorite, Person } from '~/generated/graphql';
export const favoritesState = atom<
Array<
Pick<Favorite, 'id' | 'position'> & {
company?: Pick<Company, 'id' | 'name' | 'domainName'>;
} & {
person?: Pick<Person, 'id' | 'firstName' | 'lastName' | 'avatarUrl'>;
}
>
>({
key: 'favoritesState',
default: [],
});

View File

@ -27,7 +27,7 @@ type StyledItemProps = {
soon?: boolean;
};
const StyledItem = styled.button<StyledItemProps>`
const StyledItem = styled.div<StyledItemProps>`
align-items: center;
background: ${(props) =>
props.active ? props.theme.background.transparent.light : 'inherit'};