feat: add link to relation filtered table in Record Show Page (#3261)
* feat: add link to relation filtered table in Record Show Page Closes #3125 * refactor: use generateFindManyRecordsQuery for optimization * Fixes from review * Minor fixes --------- Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
@ -4,16 +4,16 @@ import { useMapFieldMetadataToGraphQLQuery } from '@/object-metadata/hooks/useMa
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const useGenerateFindManyRecordsQuery = ({
|
||||
objectMetadataItem,
|
||||
depth,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
depth?: number;
|
||||
}) => {
|
||||
export const useGenerateFindManyRecordsQuery = () => {
|
||||
const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery();
|
||||
|
||||
return gql`
|
||||
return ({
|
||||
objectMetadataItem,
|
||||
depth,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
depth?: number;
|
||||
}) => gql`
|
||||
query FindMany${capitalize(
|
||||
objectMetadataItem.namePlural,
|
||||
)}($filter: ${capitalize(
|
||||
|
||||
@ -1,34 +1,30 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { useMapFieldMetadataToGraphQLQuery } from '@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery';
|
||||
import { EMPTY_QUERY } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
export const useGenerateFindOneRecordQuery = ({
|
||||
objectMetadataItem,
|
||||
depth,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
depth?: number;
|
||||
}) => {
|
||||
export const useGenerateFindOneRecordQuery = () => {
|
||||
const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery();
|
||||
|
||||
if (!objectMetadataItem) {
|
||||
return EMPTY_QUERY;
|
||||
}
|
||||
|
||||
return gql`
|
||||
query FindOne${objectMetadataItem.nameSingular}($objectRecordId: UUID!) {
|
||||
${objectMetadataItem.nameSingular}(filter: {
|
||||
id: {
|
||||
eq: $objectRecordId
|
||||
return ({
|
||||
objectMetadataItem,
|
||||
depth,
|
||||
}: {
|
||||
objectMetadataItem: Pick<ObjectMetadataItem, 'nameSingular' | 'fields'>;
|
||||
depth?: number;
|
||||
}) =>
|
||||
gql`
|
||||
query FindOne${objectMetadataItem.nameSingular}($objectRecordId: UUID!) {
|
||||
${objectMetadataItem.nameSingular}(filter: {
|
||||
id: {
|
||||
eq: $objectRecordId
|
||||
}
|
||||
}){
|
||||
id
|
||||
${objectMetadataItem.fields
|
||||
.map((field) => mapFieldMetadataToGraphQLQuery(field, depth))
|
||||
.join('\n')}
|
||||
}
|
||||
}){
|
||||
id
|
||||
${objectMetadataItem.fields
|
||||
.map((field) => mapFieldMetadataToGraphQLQuery(field, depth))
|
||||
.join('\n')}
|
||||
}
|
||||
}
|
||||
`;
|
||||
`;
|
||||
};
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import { useCallback, useContext, useEffect, useMemo } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { css } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import qs from 'qs';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { parseFieldRelationType } from '@/object-metadata/utils/parseFieldRelationType';
|
||||
import { FieldContext } from '@/object-record/field/contexts/FieldContext';
|
||||
import { usePersistField } from '@/object-record/field/hooks/usePersistField';
|
||||
import { entityFieldsFamilyState } from '@/object-record/field/states/entityFieldsFamilyState';
|
||||
@ -23,7 +26,10 @@ import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
||||
import { Card } from '@/ui/layout/card/components/Card';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { FilterQueryParams } from '@/views/hooks/internal/useFiltersFromQueryParams';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
|
||||
const StyledAddDropdown = styled(Dropdown)`
|
||||
margin-left: auto;
|
||||
@ -54,8 +60,23 @@ const StyledHeader = styled.header<{ isDropdownOpen?: boolean }>`
|
||||
`;
|
||||
|
||||
const StyledTitle = styled.div`
|
||||
align-items: flex-end;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledTitleLabel = styled.div`
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
padding: ${({ theme }) => theme.spacing(0, 1)};
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Link)`
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
text-decoration: none;
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
|
||||
:hover {
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
}
|
||||
`;
|
||||
|
||||
export const RecordRelationFieldCardSection = () => {
|
||||
@ -191,33 +212,54 @@ export const RecordRelationFieldCardSection = () => {
|
||||
|
||||
if (!relationLabelIdentifierFieldMetadata) return null;
|
||||
|
||||
const filterQueryParams: FilterQueryParams = {
|
||||
filter: {
|
||||
[relationFieldMetadataItem?.name || '']: {
|
||||
[ViewFilterOperand.Is]: [entityId],
|
||||
},
|
||||
},
|
||||
};
|
||||
const filterLinkHref = `/objects/${
|
||||
relationObjectMetadataItem.namePlural
|
||||
}?${qs.stringify(filterQueryParams)}`;
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<StyledHeader isDropdownOpen={isDropdownOpen}>
|
||||
<StyledTitle>{fieldDefinition.label}</StyledTitle>
|
||||
<StyledAddDropdown
|
||||
dropdownId={dropdownId}
|
||||
dropdownPlacement="right-start"
|
||||
onClose={handleCloseRelationPickerDropdown}
|
||||
clickableComponent={
|
||||
<LightIconButton
|
||||
className="displayOnHover"
|
||||
Icon={IconPlus}
|
||||
accent="tertiary"
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<SingleEntitySelectMenuItemsWithSearch
|
||||
EmptyIcon={IconForbid}
|
||||
entitiesToSelect={entities.entitiesToSelect}
|
||||
loading={entities.loading}
|
||||
onEntitySelected={handleRelationPickerEntitySelected}
|
||||
/>
|
||||
}
|
||||
dropdownHotkeyScope={{
|
||||
scope: dropdownId,
|
||||
}}
|
||||
/>
|
||||
<StyledTitle>
|
||||
<StyledTitleLabel>{fieldDefinition.label}</StyledTitleLabel>
|
||||
{parseFieldRelationType(relationFieldMetadataItem) ===
|
||||
'TO_ONE_OBJECT' && (
|
||||
<StyledLink to={filterLinkHref}>
|
||||
All ({relationRecords.length})
|
||||
</StyledLink>
|
||||
)}
|
||||
</StyledTitle>
|
||||
<DropdownScope dropdownScopeId={dropdownId}>
|
||||
<StyledAddDropdown
|
||||
dropdownId={dropdownId}
|
||||
dropdownPlacement="right-start"
|
||||
onClose={handleCloseRelationPickerDropdown}
|
||||
clickableComponent={
|
||||
<LightIconButton
|
||||
className="displayOnHover"
|
||||
Icon={IconPlus}
|
||||
accent="tertiary"
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<SingleEntitySelectMenuItemsWithSearch
|
||||
EmptyIcon={IconForbid}
|
||||
entitiesToSelect={entities.entitiesToSelect}
|
||||
loading={entities.loading}
|
||||
onEntitySelected={handleRelationPickerEntitySelected}
|
||||
/>
|
||||
}
|
||||
dropdownHotkeyScope={{
|
||||
scope: dropdownId,
|
||||
}}
|
||||
/>
|
||||
</DropdownScope>
|
||||
</StyledHeader>
|
||||
{!!relationRecords.length && (
|
||||
<Card>
|
||||
|
||||
Reference in New Issue
Block a user