From 06d610c473fd6c1ff330556c4fbf43af96f74257 Mon Sep 17 00:00:00 2001 From: Maksim Sviridov Date: Thu, 3 Aug 2023 17:47:51 +0300 Subject: [PATCH] fix(*Suggests): rewrite suggest items with new Table components --- package-lock.json | 14 +-- package.json | 2 +- src/components/GlobalSearch/GlobalSearch.tsx | 60 +++++++----- src/components/GoalCriteria/GoalCriteria.tsx | 4 +- .../GoalDependencyList/GoalDependencyList.tsx | 4 +- src/components/GoalListItemCompact.tsx | 91 ++++++------------- src/components/GoalSuggest.tsx | 86 ++++++------------ src/components/ProjectListItemCompact.tsx | 49 +++++----- trpc/router/goal.ts | 3 + trpc/router/search.ts | 5 + 10 files changed, 140 insertions(+), 178 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7b819fd27..8a2be00ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@sentry/nextjs": "7.60.0", "@tanstack/react-query": "4.29.5", "@tanstack/react-query-devtools": "4.29.5", - "@taskany/bricks": "1.27.3", + "@taskany/bricks": "1.28.0", "@taskany/colors": "1.1.0", "@taskany/icons": "1.0.0", "@tippyjs/react": "4.2.6", @@ -6053,9 +6053,9 @@ } }, "node_modules/@taskany/bricks": { - "version": "1.27.3", - "resolved": "https://registry.npmjs.org/@taskany/bricks/-/bricks-1.27.3.tgz", - "integrity": "sha512-VlA1L29ZuEX1jDz0+B6NIi+Bg2/MvPGy8e86zlBsqF9b3D55FZ4sBhXJxnrvCfVpAYKiXaploTJnfeP/Z3gWjg==", + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@taskany/bricks/-/bricks-1.28.0.tgz", + "integrity": "sha512-KmrHora/Qzl2UyuPv+gDzJDYtgJE7grtkz71cB7zZmZ7a8wgXGovVhOglf44GQeFRHE2isDh2PkhBX5S+6P0aQ==", "dependencies": { "@monaco-editor/react": "4.5.1", "@taskany/colors": "1.1.0", @@ -23252,9 +23252,9 @@ } }, "@taskany/bricks": { - "version": "1.27.3", - "resolved": "https://registry.npmjs.org/@taskany/bricks/-/bricks-1.27.3.tgz", - "integrity": "sha512-VlA1L29ZuEX1jDz0+B6NIi+Bg2/MvPGy8e86zlBsqF9b3D55FZ4sBhXJxnrvCfVpAYKiXaploTJnfeP/Z3gWjg==", + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@taskany/bricks/-/bricks-1.28.0.tgz", + "integrity": "sha512-KmrHora/Qzl2UyuPv+gDzJDYtgJE7grtkz71cB7zZmZ7a8wgXGovVhOglf44GQeFRHE2isDh2PkhBX5S+6P0aQ==", "requires": { "@monaco-editor/react": "4.5.1", "@taskany/colors": "1.1.0", diff --git a/package.json b/package.json index 5018e85f8..a9d292df5 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@sentry/nextjs": "7.60.0", "@tanstack/react-query": "4.29.5", "@tanstack/react-query-devtools": "4.29.5", - "@taskany/bricks": "1.27.3", + "@taskany/bricks": "1.28.0", "@taskany/colors": "1.1.0", "@taskany/icons": "1.0.0", "@tippyjs/react": "4.2.6", diff --git a/src/components/GlobalSearch/GlobalSearch.tsx b/src/components/GlobalSearch/GlobalSearch.tsx index 14c11ab66..46e7340fc 100644 --- a/src/components/GlobalSearch/GlobalSearch.tsx +++ b/src/components/GlobalSearch/GlobalSearch.tsx @@ -1,16 +1,16 @@ /* eslint-disable no-nested-ternary */ import React, { useState, ChangeEvent, useCallback, useMemo, useRef } from 'react'; import styled from 'styled-components'; -import { ComboBox, Input, SearchIcon, Text } from '@taskany/bricks'; -import { gray7, textColor } from '@taskany/colors'; +import { ComboBox, Input, SearchIcon, Text, Table } from '@taskany/bricks'; +import { gapS, gapXs, gray7, radiusM, textColor } from '@taskany/colors'; +import NextLink from 'next/link'; import { trpc } from '../../utils/trpcClient'; -import { useRouter } from '../../hooks/router'; +import { routes, useRouter } from '../../hooks/router'; import { useHotkey } from '../../hooks/useHotkeys'; import { Keyboard } from '../Keyboard'; import { GoalListItemCompact } from '../GoalListItemCompact'; import { ProjectListItemCompact } from '../ProjectListItemCompact'; -import { Table } from '../Table'; import { tr } from './GlobalSearch.i18n'; @@ -34,8 +34,10 @@ const StyledTrigger = styled(StyledSearchIcon)` } `; -const StyledResultsTable = styled(Table)` - grid-template-columns: 1fr minmax(300px, 30%) repeat(4, max-content) 1fr; +const StyledRow = styled(GoalListItemCompact)` + padding: ${gapXs} ${gapS}; + border-radius: ${radiusM}; + cursor: pointer; `; enum ItemType { @@ -120,26 +122,42 @@ export const GlobalSearch = React.forwardRef((_, ref) => { {...props} /> )} - renderItems={(children) => ( - {children as React.ReactNode} - )} + renderItems={(children) => {children as React.ReactNode}
} renderItem={(props) => { switch (props.item.__kind) { case ItemType.goal: return ( - + passHref + href={routes.goal(props.item._shortId)} + legacyBehavior + > + + ); case ItemType.project: diff --git a/src/components/GoalCriteria/GoalCriteria.tsx b/src/components/GoalCriteria/GoalCriteria.tsx index 8919731fd..b004ccb7a 100644 --- a/src/components/GoalCriteria/GoalCriteria.tsx +++ b/src/components/GoalCriteria/GoalCriteria.tsx @@ -24,7 +24,7 @@ import { GoalAchiveCriteria } from '../../../trpc/inferredTypes'; import { ActivityFeedItem } from '../ActivityFeed'; import { Circle, CircledIcon } from '../Circle'; import { UserGroup } from '../UserGroup'; -import { GoalListItemCompactCustomize, CustomCell } from '../GoalListItemCompact'; +import { GoalListItemCompact, CustomCell } from '../GoalListItemCompact'; import { routes } from '../../hooks/router'; import { AddCriteriaForm, EditCriteriaForm } from '../CriteriaForm/CriteriaForm'; @@ -77,7 +77,7 @@ const StyledCheckboxWrapper = styled.span<{ canEdit: boolean }>` `} `; -const StyledTableRow = styled(GoalListItemCompactCustomize)` +const StyledTableRow = styled(GoalListItemCompact)` padding: 3px 0 4px; `; diff --git a/src/components/GoalDependencyList/GoalDependencyList.tsx b/src/components/GoalDependencyList/GoalDependencyList.tsx index 11d1c6347..edeb6c149 100644 --- a/src/components/GoalDependencyList/GoalDependencyList.tsx +++ b/src/components/GoalDependencyList/GoalDependencyList.tsx @@ -11,7 +11,7 @@ import { ToggleGoalDependency, dependencyKind } from '../../schema/goal'; import { ActivityByIdReturnType, GoalDependencyItem } from '../../../trpc/inferredTypes'; import { Circle, CircledIcon as CircleIconInner } from '../Circle'; import { ContentItem, Title } from '../Table'; -import { CustomCell, GoalListItemCompactCustomize } from '../GoalListItemCompact'; +import { CustomCell, GoalListItemCompact } from '../GoalListItemCompact'; import { routes } from '../../hooks/router'; import { UserGroup } from '../UserGroup'; import { BetaBadge } from '../BetaBadge'; @@ -41,7 +41,7 @@ const StyledTable = styled(Table)` margin-bottom: 10px; `; -const StyledTableRow = styled(GoalListItemCompactCustomize)` +const StyledTableRow = styled(GoalListItemCompact)` position: relative; &:hover { diff --git a/src/components/GoalListItemCompact.tsx b/src/components/GoalListItemCompact.tsx index 817ac644c..bfc8f894d 100644 --- a/src/components/GoalListItemCompact.tsx +++ b/src/components/GoalListItemCompact.tsx @@ -1,25 +1,25 @@ import React, { MouseEventHandler, useMemo } from 'react'; -import NextLink from 'next/link'; import styled, { css } from 'styled-components'; import { nullable, GoalIcon, Dropdown, MenuItem, - TableRow as BrickTableRow, - TableCell as BrickTableCel, + TableRow, + TableCell, + TableRowProps, + TableCellProps, } from '@taskany/bricks'; import { IconMoreVerticalOutline } from '@taskany/icons'; import type { Estimate, State as StateType } from '@prisma/client'; -import { routes } from '../hooks/router'; import { Priority } from '../types/priority'; import { ActivityByIdReturnType } from '../../trpc/inferredTypes'; import { estimateToString } from '../utils/estimateToString'; import { getPriorityText } from './PriorityText/PriorityText'; import { UserGroup } from './UserGroup'; -import { TableRow, ContentItem, TitleItem, TitleContainer, Title, TextItem } from './Table'; +import { Title, TextItem } from './Table'; import { StateDot } from './StateDot'; interface CommonGoalListItemCompactProps { @@ -39,55 +39,6 @@ type GoalListItemCompactProps = { onClick?: MouseEventHandler; } & CommonGoalListItemCompactProps; -export const GoalListItemCompact: React.FC = React.memo( - ({ shortId, projectId, owner, issuer, title, state, focused, estimate, priority, className, onClick }) => { - const issuers = useMemo(() => { - if (issuer && owner && owner.id === issuer.id) { - return [owner]; - } - - return [issuer, owner].filter(Boolean) as NonNullable[]; - }, [issuer, owner]); - - return ( - - - - - - - - - {title} - - - - - {nullable(state, (s) => ( - - ))} - - - {getPriorityText(priority as Priority)} - - - - {projectId} - - - - - - - - {nullable(estimate, (e) => estimateToString(e))} - - - - ); - }, -); - type ColumnRenderProps> = T & Omit & { issuers: Array; @@ -96,7 +47,7 @@ type ColumnRenderProps> = T & type GoalListItemCompactColumnProps = { name: 'icon' | 'title' | 'state' | 'priority' | 'projectId' | 'issuers' | 'estimate' | string; renderColumn?: (props: T) => React.ReactElement; - columnProps?: React.ComponentProps; + columnProps?: TableCellProps; }; interface GoalItemAction { @@ -121,7 +72,9 @@ interface GoalListItemCompactCustomizeProps> { } interface GoalListItemCompactCustomizeRender { - >(props: GoalListItemCompactCustomizeProps): React.ReactElement; + >( + props: GoalListItemCompactCustomizeProps & Omit, + ): React.ReactElement; } interface RenderColumnProps { @@ -133,7 +86,7 @@ interface ColumnRender { (props: RenderColumnProps): React.ReactNode; } -const StyledCell = styled(BrickTableCel)<{ forIcon?: boolean }>` +const StyledCell = styled(TableCell)<{ forIcon?: boolean }>` ${({ forIcon }) => forIcon && css` @@ -208,14 +161,10 @@ const Column: ColumnRender = ({ col, componentProps }) => { return null; } - return nullable(content, (c) => ( - - {c} - - )); + return nullable(content, (c) => {c}); }; -export const GoalListItemCompactCustomize: GoalListItemCompactCustomizeRender = ({ +export const GoalListItemCompact: GoalListItemCompactCustomizeRender = ({ columns, actions, rowIcon, @@ -223,9 +172,21 @@ export const GoalListItemCompactCustomize: GoalListItemCompactCustomizeRender = onClick, className, item, + gap = 7, + align, + justify, + focused, }) => { return ( - + {rowIcon || } @@ -255,7 +216,7 @@ export const GoalListItemCompactCustomize: GoalListItemCompactCustomizeRender = ))} - + ); }; diff --git a/src/components/GoalSuggest.tsx b/src/components/GoalSuggest.tsx index 563f2085c..6b68417a8 100644 --- a/src/components/GoalSuggest.tsx +++ b/src/components/GoalSuggest.tsx @@ -1,22 +1,13 @@ -import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { forwardRef, useCallback, useEffect, useState } from 'react'; import styled from 'styled-components'; -import { ComboBox, GoalIcon, nullable } from '@taskany/bricks'; +import { ComboBox, Table } from '@taskany/bricks'; +import { radiusM, gapS, gapXs } from '@taskany/colors'; import { Estimate, State as StateType } from '@prisma/client'; import { trpc } from '../utils/trpcClient'; import { ActivityByIdReturnType } from '../../trpc/inferredTypes'; -import { estimateToString } from '../utils/estimateToString'; -import { TableRow, ContentItem, TitleItem, TitleContainer, Title, TextItem, Table } from './Table'; -import { StateDot } from './StateDot'; -import { UserGroup } from './UserGroup'; - -const StyledTable = styled(Table)` - display: grid; - grid-template-columns: 35px minmax(250px, 20%) repeat(4, max-content); - padding: 0; - margin: 0; -`; +import { GoalListItemCompact } from './GoalListItemCompact'; interface GoalSuggestProps { onChange: (val: any) => void; @@ -26,6 +17,11 @@ interface GoalSuggestProps { className?: string; } +const StyledTableRow = styled(GoalListItemCompact)` + border-radius: ${radiusM}; + padding: ${gapXs} ${gapS}; +`; + interface GoalSuggestItemProps { projectId: string | null; title: string; @@ -37,50 +33,22 @@ interface GoalSuggestItemProps { onClick?: () => void; } -const GoalSuggestItem: React.FC = ({ - title, - projectId, - owner, - issuer, - state, - estimate, - focused, - onClick, -}) => { - const rowRef = useRef(null); - const issuers = useMemo(() => { - if (issuer && owner && owner.id === issuer.id) { - return [owner]; - } - - return [issuer, owner].filter(Boolean) as NonNullable[]; - }, [issuer, owner]); - +const GoalSuggestItem: React.FC = ({ focused, onClick, ...props }) => { return ( - - - - - - - {title} - - - - {nullable(state, (s) => ( - - ))} - - - {projectId} - - - - - - {nullable(estimate, (e) => estimateToString(e))} - - + ); }; @@ -122,7 +90,7 @@ export const GoalSuggest = forwardRef( items={items} visible={visible && showSuggest} renderInput={renderInput} - maxWidth={600} + maxWidth={550} renderItem={({ item, cursor, index, onClick }) => ( ( state={item.state} issuer={item.activity} owner={item.owner} - estimate={item._lastEstimate} + estimate={item.estimate[item.estimate.length - 1]?.estimate ?? undefined} /> )} - renderItems={(children) => {children as React.ReactNode}} + renderItems={(children) => {children as React.ReactNode}
} /> ); }, diff --git a/src/components/ProjectListItemCompact.tsx b/src/components/ProjectListItemCompact.tsx index 88ae9b8fd..1a0e9cfab 100644 --- a/src/components/ProjectListItemCompact.tsx +++ b/src/components/ProjectListItemCompact.tsx @@ -1,12 +1,14 @@ import React, { MouseEventHandler } from 'react'; import NextLink from 'next/link'; -import { ProjectIcon } from '@taskany/bricks'; +import styled from 'styled-components'; +import { ProjectIcon, TableRow, TableCell } from '@taskany/bricks'; +import { gapS, gapXs, radiusM } from '@taskany/colors'; import { routes } from '../hooks/router'; import { ActivityByIdReturnType } from '../../trpc/inferredTypes'; import { UserGroup } from './UserGroup'; -import { TableRow, ContentItem, TitleItem, TitleContainer, Title, TextItem } from './Table'; +import { TitleContainer, Title, TextItem } from './Table'; interface ProjectListItemCompactProps { id: string; @@ -14,39 +16,44 @@ interface ProjectListItemCompactProps { owner?: ActivityByIdReturnType; focused?: boolean; className?: string; - onClick?: MouseEventHandler; + onClick?: MouseEventHandler; } +const StyledRow = styled(TableRow)` + padding: ${gapXs} ${gapS}; + border-radius: ${radiusM}; + cursor: pointer; +`; + export const ProjectListItemCompact: React.FC = React.memo( ({ id, owner, title, focused, className, onClick }) => { return ( - - + + - - - + + {title} - - - - - - + + {id} - - - + + []} /> - - - - + +
); }, diff --git a/trpc/router/goal.ts b/trpc/router/goal.ts index 1f809dd9b..75deb28af 100644 --- a/trpc/router/goal.ts +++ b/trpc/router/goal.ts @@ -100,6 +100,9 @@ export const goal = router({ }, include: { ...goalDeepQuery, + estimate: { + include: { estimate: true }, + }, }, }); }), diff --git a/trpc/router/search.ts b/trpc/router/search.ts index 1ca14006c..842665697 100644 --- a/trpc/router/search.ts +++ b/trpc/router/search.ts @@ -36,6 +36,11 @@ export const search = router({ }, include: { ...goalDeepQuery, + estimate: { + include: { + estimate: true, + }, + }, }, }), prisma.project.findMany({