Skip to content

Commit

Permalink
feat(Search): add more entities & misc improvements (#10883)
Browse files Browse the repository at this point in the history
  • Loading branch information
Betree authored Dec 31, 2024
1 parent 97f4056 commit db0e7a0
Show file tree
Hide file tree
Showing 15 changed files with 2,594 additions and 11,865 deletions.
86 changes: 76 additions & 10 deletions components/search/SearchCommand.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,30 @@ import { API_V2_CONTEXT } from '../../lib/graphql/helpers';
import useDebouncedValue from '../../lib/hooks/useDebouncedValue';
import useLoggedInUser from '../../lib/hooks/useLoggedInUser';
import useQueryFilter from '../../lib/hooks/useQueryFilter';
import { getCollectivePageRoute, getCommentUrl, getExpensePageUrl } from '../../lib/url-helpers';

import { DashboardContext } from '../dashboard/DashboardContext';
import { getMenuItems } from '../dashboard/Menu';
import Link from '../Link';
import StyledSpinner from '../StyledSpinner';
import { CommandDialog, CommandGroup, CommandItem, CommandList } from '../ui/Command';
import { useWorkspace } from '../WorkspaceProvider';

import { AccountResult } from './AccountResult';
import { AccountResult } from './result/AccountResult';
import { CommentResult } from './result/CommentResult';
import { ExpenseResult } from './result/ExpenseResult';
import { OrderResult } from './result/OrderResult';
import { TransactionResult } from './result/TransactionResult';
import { UpdateResult } from './result/UpdateResult';
import { ContextPill } from './ContextPill';
import { ExpenseResult } from './ExpenseResult';
import { PageResult } from './PageResult';
import { searchCommandQuery } from './queries';
import { SearchCommandGroup } from './SearchCommandGroup';
import { SearchCommandLegend } from './SearchCommandLegend';
import { TransactionResult } from './TransactionResult';
import type { PageVisit } from './useRecentlyVisited';
import { useRecentlyVisited } from './useRecentlyVisited';

// TODO i18n
export const SearchCommand = ({ open, setOpen }) => {
const router = useRouter();
const intl = useIntl();
Expand Down Expand Up @@ -78,7 +84,7 @@ export const SearchCommand = ({ open, setOpen }) => {
}, [open]);

const [search, { data, loading }] = useLazyQuery(searchCommandQuery, {
variables: queryFilter.variables,
variables: { ...queryFilter.variables, imageHeight: 72 },
notifyOnNetworkStatusChange: true,
context: API_V2_CONTEXT,
fetchPolicy: 'cache-and-network',
Expand Down Expand Up @@ -135,6 +141,18 @@ export const SearchCommand = ({ open, setOpen }) => {
}
addToRecent({ key: data.legacyId.toString(), type, data });
break;
case 'comment':
router.push(getCommentUrl(data));
// Skip adding comments to recent
break;
case 'order':
router.push(`/dashboard/${workspace.slug}/contributions/${data.legacyId}`);
addToRecent({ key: data.legacyId.toString(), type, data });
break;
case 'update':
router.push(`/dashboard/${workspace.slug}/updates/${data.legacyId}`);
addToRecent({ key: data.legacyId.toString(), type, data });
break;
case 'page':
router.push(`/dashboard/${workspace.slug}/${data.section}`);
// Skip adding dashboard pages to recent
Expand Down Expand Up @@ -168,6 +186,7 @@ export const SearchCommand = ({ open, setOpen }) => {
menuItem.label.toString().toLowerCase().includes(debouncedInput.toLowerCase()),
);
}, [debouncedInput, flattenedMenuItems, queryFilter.values.context]);

return (
<CommandDialog open={open} onOpenChange={setOpen} shouldFilter={false} onKeyDown={handleKeyDown}>
{/* eslint-disable-next-line react/no-unknown-property */}
Expand Down Expand Up @@ -198,7 +217,9 @@ export const SearchCommand = ({ open, setOpen }) => {
<CommandItem key={recentVisit.key} className="gap-2" onSelect={() => handleResultSelect(recentVisit)}>
{recentVisit.type === 'account' && <AccountResult account={recentVisit.data} />}
{recentVisit.type === 'expense' && <ExpenseResult expense={recentVisit.data} />}
{recentVisit.type === 'order' && <OrderResult order={recentVisit.data} />}
{recentVisit.type === 'transaction' && <TransactionResult transaction={recentVisit.data} />}
{recentVisit.type === 'update' && <UpdateResult update={recentVisit.data} />}
</CommandItem>
))}
</CommandGroup>
Expand All @@ -220,9 +241,11 @@ export const SearchCommand = ({ open, setOpen }) => {
input={debouncedInput}
nodes={data?.search.results.accounts.collection.nodes}
renderNode={account => (
<CommandItem key={account.id} onSelect={() => handleResultSelect({ type: 'account', data: account })}>
<AccountResult account={account} highlights={data.search.results.accounts.highlights[account.id]} />
</CommandItem>
<Link href={getCollectivePageRoute(account)} onClick={e => e.preventDefault()}>
<CommandItem key={account.id} onSelect={() => handleResultSelect({ type: 'account', data: account })}>
<AccountResult account={account} highlights={data.search.results.accounts.highlights[account.id]} />
</CommandItem>
</Link>
)}
/>

Expand All @@ -232,11 +255,28 @@ export const SearchCommand = ({ open, setOpen }) => {
nodes={data?.search.results.expenses.collection.nodes}
input={debouncedInput}
renderNode={expense => (
<CommandItem key={expense.id} onSelect={() => handleResultSelect({ type: 'expense', data: expense })}>
<ExpenseResult expense={expense} highlights={data.search.results.expenses.highlights[expense.id]} />
</CommandItem>
<Link href={getExpensePageUrl(expense)} onClick={e => e.preventDefault()}>
<CommandItem key={expense.id} onSelect={() => handleResultSelect({ type: 'expense', data: expense })}>
<ExpenseResult expense={expense} highlights={data.search.results.expenses.highlights[expense.id]} />
</CommandItem>
</Link>
)}
/>
{data?.search.results.orders && (
<SearchCommandGroup
label="Contributions"
input={debouncedInput}
totalCount={data?.search.results.orders.collection.totalCount}
nodes={data?.search.results.orders.collection.nodes}
renderNode={order => (
<Link href={getCollectivePageRoute(order.account)} onClick={e => e.preventDefault()}>
<CommandItem key={order.id} onSelect={() => handleResultSelect({ type: 'order', data: order })}>
<OrderResult order={order} highlights={data.search.results.orders.highlights[order.id]} />
</CommandItem>
</Link>
)}
/>
)}
{data?.search.results.transactions && (
<SearchCommandGroup
label="Transactions"
Expand All @@ -256,6 +296,32 @@ export const SearchCommand = ({ open, setOpen }) => {
)}
/>
)}
<SearchCommandGroup
label="Updates"
input={debouncedInput}
totalCount={data?.search.results.updates.collection.totalCount}
nodes={data?.search.results.updates.collection.nodes}
renderNode={update => (
<Link href={getCollectivePageRoute(update.account)} onClick={e => e.preventDefault()}>
<CommandItem key={update.id} onSelect={() => handleResultSelect({ type: 'update', data: update })}>
<UpdateResult update={update} highlights={data.search.results.updates.highlights[update.id]} />
</CommandItem>
</Link>
)}
/>
<SearchCommandGroup
label="Comments"
input={debouncedInput}
totalCount={data?.search.results.comments.collection.totalCount}
nodes={data?.search.results.comments.collection.nodes}
renderNode={comment => (
<Link href={getCommentUrl(comment)} onClick={e => e.preventDefault()}>
<CommandItem key={comment.id} onSelect={() => handleResultSelect({ type: 'comment', data: comment })}>
<CommentResult comment={comment} highlights={data.search.results.comments.highlights[comment.id]} />
</CommandItem>
</Link>
)}
/>
</CommandList>
<SearchCommandLegend />
</CommandDialog>
Expand Down
142 changes: 137 additions & 5 deletions components/search/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const searchCommandQuery = gql`
$account: AccountReferenceInput
$limit: Int!
$includeTransactions: Boolean!
$imageHeight: Int
) {
search(searchTerm: $searchTerm, defaultLimit: $limit, host: $host, account: $account) {
results {
Expand All @@ -19,11 +20,93 @@ export const searchCommandQuery = gql`
id
name
slug
imageUrl
imageUrl(height: $imageHeight)
type
}
}
}
comments {
highlights
collection {
totalCount
limit
nodes {
id
html
createdAt
fromAccount {
id
slug
name
imageUrl(height: $imageHeight)
type
}
expense {
id
legacyId
description
account {
id
name
slug
imageUrl(height: $imageHeight)
type
}
}
update {
id
legacyId
title
account {
id
slug
name
imageUrl(height: $imageHeight)
type
}
}
order {
id
legacyId
toAccount {
id
slug
name
imageUrl(height: $imageHeight)
type
}
}
hostApplication {
id
account {
id
slug
name
imageUrl(height: $imageHeight)
type
}
host {
id
slug
name
imageUrl(height: $imageHeight)
type
}
}
conversation {
id
slug
account {
id
slug
name
imageUrl(height: $imageHeight)
type
}
}
}
}
}
expenses {
highlights
collection {
Expand All @@ -43,14 +126,45 @@ export const searchCommandQuery = gql`
id
name
slug
imageUrl
imageUrl(height: $imageHeight)
type
}
account {
id
name
slug
imageUrl
imageUrl(height: $imageHeight)
type
}
}
}
}
orders @include(if: $includeTransactions) {
highlights
collection {
totalCount
limit
nodes {
id
legacyId
description
status
amount {
valueInCents
currency
}
toAccount {
id
slug
name
imageUrl(height: $imageHeight)
type
}
fromAccount {
id
slug
name
imageUrl(height: $imageHeight)
type
}
}
Expand All @@ -75,19 +189,37 @@ export const searchCommandQuery = gql`
id
slug
name
imageUrl
imageUrl(height: $imageHeight)
type
}
oppositeAccount {
id
slug
name
imageUrl
imageUrl(height: $imageHeight)
type
}
}
}
}
updates {
highlights
collection {
totalCount
limit
nodes {
id
legacyId
title
account {
id
slug
name
}
}
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ import React from 'react';
import { Markup } from 'interweave';
import { useIntl } from 'react-intl';

import formatCollectiveType from '../../lib/i18n/collective-type';
import formatCollectiveType from '../../../lib/i18n/collective-type';

import Avatar from '../Avatar';
import { Badge } from '../ui/Badge';

import { getHighlightsFields } from './lib';
import type { SearchHighlights } from './types';
import type { AccountResultData } from './useRecentlyVisited';
import Avatar from '../../Avatar';
import { Badge } from '../../ui/Badge';
import { getHighlightsFields } from '../lib';
import type { SearchHighlights } from '../types';
import type { AccountResultData } from '../useRecentlyVisited';

export function AccountResult({ account, highlights }: { account: AccountResultData; highlights?: SearchHighlights }) {
const intl = useIntl();
Expand All @@ -32,7 +31,7 @@ export function AccountResult({ account, highlights }: { account: AccountResultD
{formatCollectiveType(intl, account.type)}
</Badge>
</div>
<div className="truncate text-muted-foreground">
<div className="mt-1 truncate text-muted-foreground">
@
{highlightFields.top.slug ? (
<Markup allowList={['mark']} content={highlightFields.top.slug[0]} />
Expand Down
Loading

0 comments on commit db0e7a0

Please sign in to comment.