From 06c3588741394457f06f996334e4384ae605ed58 Mon Sep 17 00:00:00 2001 From: Maksim Sviridov Date: Thu, 8 Aug 2024 14:16:59 +0300 Subject: [PATCH] fix(Kanban,GoalTableList): data mapping to props --- .../DashboardPage/DashboardPage.tsx | 66 ++++++------------- src/components/FlatGoalList.tsx | 47 +++---------- .../GoalTableList/GoalTableList.tsx | 26 ++++++++ .../GoalsKanbanCard/GoalsKanbanCard.tsx | 2 +- src/components/Kanban/Kanban.tsx | 66 ++++++++++++------- src/components/ProjectListItemConnected.tsx | 66 ++++++------------- src/utils/kanban.ts | 19 ------ trpc/router/projectV2.ts | 2 +- 8 files changed, 120 insertions(+), 174 deletions(-) delete mode 100644 src/utils/kanban.ts diff --git a/src/components/DashboardPage/DashboardPage.tsx b/src/components/DashboardPage/DashboardPage.tsx index 8581f4bca..ab1530f81 100644 --- a/src/components/DashboardPage/DashboardPage.tsx +++ b/src/components/DashboardPage/DashboardPage.tsx @@ -1,4 +1,4 @@ -import React, { ComponentProps, useCallback, useEffect, useMemo } from 'react'; +import React, { useCallback, useEffect, useMemo } from 'react'; import { nullable } from '@taskany/bricks'; import { ListView, TreeViewElement } from '@taskany/bricks/harmony'; @@ -9,7 +9,6 @@ import { useUrlFilterParams } from '../../hooks/useUrlFilterParams'; import { useFiltersPreset } from '../../hooks/useFiltersPreset'; import { GoalByIdReturnType } from '../../../trpc/inferredTypes'; import { trpc } from '../../utils/trpcClient'; -import { buildKanban } from '../../utils/kanban'; import { getPageTitle } from '../../utils/getPageTitle'; import { Page } from '../Page/Page'; import { useGoalPreview } from '../GoalPreview/GoalPreviewProvider'; @@ -18,10 +17,10 @@ import { LoadMoreButton } from '../LoadMoreButton/LoadMoreButton'; import { InlineCreateGoalControl } from '../InlineCreateGoalControl/InlineCreateGoalControl'; import { ProjectListItemCollapsable } from '../ProjectListItemCollapsable/ProjectListItemCollapsable'; import { routes } from '../../hooks/router'; -import { GoalTableList } from '../GoalTableList/GoalTableList'; +import { GoalTableList, mapToRenderProps } from '../GoalTableList/GoalTableList'; import { PresetModals } from '../PresetModals'; import { FiltersPanel } from '../FiltersPanel/FiltersPanel'; -import { Kanban } from '../Kanban/Kanban'; +import { Kanban, buildKanban } from '../Kanban/Kanban'; import { safeUserData } from '../../utils/getUserName'; import { tr } from './DashboardPage.i18n'; @@ -55,8 +54,14 @@ export const DashboardPage = ({ user, ssrTime, defaultPresetFallback }: External return acc; }, []); - const canbans = gr.reduce['value']>>((acum, project) => { - acum[project.id] = buildKanban(project.goals ?? []); + const canbans = gr.reduce['value']>>((acum, project) => { + acum[project.id] = buildKanban(project.goals ?? [], (goal) => ({ + ...goal, + shortId: goal._shortId, + id: goal.id, + commentsCount: goal._count.comments ?? 0, + progress: goal._achivedCriteriaWeight, + })); return acum; }, {}); @@ -125,47 +130,14 @@ export const DashboardPage = ({ user, ssrTime, defaultPresetFallback }: External nullable(goals, (g) => ( ({ - title, - id, - shortId: _shortId, - commentsCount: _counts?.comments, - tags, - updatedAt, - owner: safeUserData(owner), - participants: participants?.map(safeUserData), - state, - estimate: estimate - ? { - value: estimate, - type: estimateType, - } - : null, - priority: priority.title, - achievedCriteriaWeight: _achivedCriteriaWeight, - partnershipProjects, - isInPartnerProject: project.id !== projectId, - project: parent, - }), - )} + goals={mapToRenderProps(g, (goal) => ({ + ...goal, + shortId: goal._shortId, + commentsCount: goal._count.comments, + owner: safeUserData(goal.owner), + participants: goal.participants?.map(safeUserData), + achievedCriteriaWeight: goal._achivedCriteriaWeight, + }))} /> )), diff --git a/src/components/FlatGoalList.tsx b/src/components/FlatGoalList.tsx index 57422622c..7a4dc5413 100644 --- a/src/components/FlatGoalList.tsx +++ b/src/components/FlatGoalList.tsx @@ -6,10 +6,10 @@ import { useUrlFilterParams } from '../hooks/useUrlFilterParams'; import { trpc } from '../utils/trpcClient'; import { refreshInterval } from '../utils/config'; import { useFMPMetric } from '../utils/telemetry'; -import { GoalByIdReturnType } from '../../trpc/inferredTypes'; +import { FilterById, GoalByIdReturnType } from '../../trpc/inferredTypes'; import { safeUserData } from '../utils/getUserName'; -import { GoalTableList } from './GoalTableList/GoalTableList'; +import { GoalTableList, mapToRenderProps } from './GoalTableList/GoalTableList'; import { useGoalPreview } from './GoalPreview/GoalPreviewProvider'; import { LoadMoreButton } from './LoadMoreButton/LoadMoreButton'; @@ -74,41 +74,14 @@ export const FlatGoalList: React.FC = ({ filterPreset }) => { {nullable(goalsOnScreen, (goals) => ( ({ - title, - id, - shortId: _shortId, - commentsCount: _count?.comments, - tags, - updatedAt, - owner: safeUserData(owner), - participants: participants?.map(safeUserData), - state, - estimate: estimate - ? { - value: estimate, - type: estimateType, - } - : null, - priority: priority?.title, - achievedCriteriaWeight: _achivedCriteriaWeight, - }), - )} + goals={mapToRenderProps(goals, (goal) => ({ + ...goal, + owner: safeUserData(goal.owner), + participants: goal.participants?.map(safeUserData), + shortId: goal._shortId, + commentsCount: goal._count.comments, + achievedCriteriaWeight: goal._achivedCriteriaWeight, + }))} onTagClick={setTagsFilterOutside} /> ))} diff --git a/src/components/GoalTableList/GoalTableList.tsx b/src/components/GoalTableList/GoalTableList.tsx index a087f27bc..7bf3822f7 100644 --- a/src/components/GoalTableList/GoalTableList.tsx +++ b/src/components/GoalTableList/GoalTableList.tsx @@ -3,6 +3,7 @@ import { MouseEventHandler, useCallback, useEffect, useMemo } from 'react'; import { nullable } from '@taskany/bricks'; import { IconGitBranchOutline, IconMessageTextOutline } from '@taskany/icons'; +import { DateType } from '../../../generated/kysely/types'; import { State as StateType } from '../../../trpc/inferredTypes'; import { TableListItem, TableListItemElement } from '../TableListItem/TableListItem'; import { safeUserData } from '../../utils/getUserName'; @@ -234,3 +235,28 @@ export const GoalTableList = ({ ); }; + +type GoalSource = IdentifierRecord & { + priority: IdentifierRecord | null; + estimate: Date | null; + estimateType: DateType | null; + [key: string]: unknown; +}; + +export function mapToRenderProps( + goals: T[], + mapper: (val: T1 & Pick) => GoalTableListItem, +): GoalTableListItem[] { + return goals.map((v) => + mapper({ + ...v, + priority: v.priority?.title, + estimate: v.estimate + ? { + value: v.estimate, + type: v.estimateType, + } + : null, + }), + ); +} diff --git a/src/components/GoalsKanbanCard/GoalsKanbanCard.tsx b/src/components/GoalsKanbanCard/GoalsKanbanCard.tsx index 86838c44f..1b3c002c6 100644 --- a/src/components/GoalsKanbanCard/GoalsKanbanCard.tsx +++ b/src/components/GoalsKanbanCard/GoalsKanbanCard.tsx @@ -33,7 +33,7 @@ interface GoalsKanbanCardProps extends HTMLAttributes { owner?: ActivityByIdReturnType | null; estimate?: Date | null; estimateType?: DateType | null; - tags?: TagObject[]; + tags?: TagObject[] | null; onTagClick?: (tag: { id: string }) => MouseEventHandler; priority?: Priority | null; progress: number | null; diff --git a/src/components/Kanban/Kanban.tsx b/src/components/Kanban/Kanban.tsx index dc5da0adb..8b3465177 100644 --- a/src/components/Kanban/Kanban.tsx +++ b/src/components/Kanban/Kanban.tsx @@ -1,8 +1,8 @@ -import { MouseEventHandler, useCallback } from 'react'; +import React, { MouseEventHandler, useCallback } from 'react'; import { KanbanColumn, KanbanContainer } from '@taskany/bricks/harmony'; import { trpc } from '../../utils/trpcClient'; -import { FilterById, DashboardGoal } from '../../../trpc/inferredTypes'; +import { FilterById } from '../../../trpc/inferredTypes'; import { useUrlFilterParams } from '../../hooks/useUrlFilterParams'; import { NextLink } from '../NextLink'; import { State as KanbanState } from '../State'; @@ -12,12 +12,17 @@ import { routes } from '../../hooks/router'; import s from './Kanban.module.css'; -interface KanbanProps { - value: Record; +type KanbanItemProps = Omit< + React.ComponentProps, + keyof React.HTMLAttributes +> & { id: string; shortId: string; title: string }; + +interface KanbanProps { + value: Record; filterPreset?: FilterById; } -export const Kanban = >({ value, filterPreset }: KanbanProps) => { +export const Kanban = ({ value, filterPreset }: KanbanProps) => { const { data: states = [] } = trpc.state.all.useQuery(); const { queryState, setTagsFilterOutside } = useUrlFilterParams({ @@ -54,30 +59,18 @@ export const Kanban = >({ value, filterPres - {goals.map((g) => { - const { project: _, ...goal } = g; - + {goals.map((goal) => { return ( - + ); })} @@ -87,3 +80,28 @@ export const Kanban = >({ value, filterPres ); }; + +type StateIdRecord = Record<'stateId', string>; + +type NullableStateId = StateIdRecord | Record<'stateId', null>; + +export const buildKanban = KanbanItemProps>( + goals: T[], + mapper: M, +): Record => { + return goals.reduce>((acum, goal) => { + const stateKey = goal.stateId; + + if (!stateKey) { + return acum; + } + + if (!acum[stateKey]) { + acum[stateKey] = []; + } + + acum[stateKey].push(mapper(goal)); + + return acum; + }, {}); +}; diff --git a/src/components/ProjectListItemConnected.tsx b/src/components/ProjectListItemConnected.tsx index a3cd4534a..51c999c51 100644 --- a/src/components/ProjectListItemConnected.tsx +++ b/src/components/ProjectListItemConnected.tsx @@ -4,17 +4,16 @@ import { TreeViewElement } from '@taskany/bricks/harmony'; import { FilterById, GoalByIdReturnType } from '../../trpc/inferredTypes'; import { trpc } from '../utils/trpcClient'; -import { buildKanban } from '../utils/kanban'; import { useUrlFilterParams } from '../hooks/useUrlFilterParams'; import { refreshInterval } from '../utils/config'; import { routes } from '../hooks/router'; import { safeUserData } from '../utils/getUserName'; -import { GoalTableList } from './GoalTableList/GoalTableList'; +import { GoalTableList, mapToRenderProps } from './GoalTableList/GoalTableList'; import { ProjectListItemCollapsable } from './ProjectListItemCollapsable/ProjectListItemCollapsable'; import { InlineCreateGoalControl } from './InlineCreateGoalControl/InlineCreateGoalControl'; import { useGoalPreview } from './GoalPreview/GoalPreviewProvider'; -import { Kanban } from './Kanban/Kanban'; +import { Kanban, buildKanban } from './Kanban/Kanban'; interface ProjectListItemConnectedProps extends ComponentProps { parent?: ComponentProps['project']; @@ -78,7 +77,17 @@ export const ProjectListItemConnected: FC = ({ }; }, [on, projectDeepInfo?.goals, utils.project.getByIds, utils.project.getDeepInfo]); - const kanban = useMemo(() => buildKanban(projectDeepInfo?.goals || []), [projectDeepInfo]); + const kanban = useMemo( + () => + buildKanban(projectDeepInfo?.goals ?? [], (goal) => ({ + ...goal, + shortId: goal._shortId, + id: goal.id, + commentsCount: goal._count.comments ?? 0, + progress: goal._achivedCriteriaWeight, + })), + [projectDeepInfo], + ); const subNodes = useMemo( () => @@ -107,47 +116,14 @@ export const ProjectListItemConnected: FC = ({ ) : ( ({ - title, - id, - shortId: _shortId, - commentsCount: _count?.comments, - tags, - updatedAt, - owner: safeUserData(owner), - participants: participants?.map(safeUserData), - state, - estimate: estimate - ? { - value: estimate, - type: estimateType, - } - : null, - priority: priority?.title, - achievedCriteriaWeight: _achivedCriteriaWeight, - partnershipProjects, - isInPartnerProject: project.id !== projectId, - project: parent, - }), - )} + goals={mapToRenderProps(goals, (goal) => ({ + ...goal, + shortId: goal._shortId, + commentsCount: goal._count.comments, + owner: safeUserData(goal.owner), + participants: goal.participants?.map(safeUserData), + achievedCriteriaWeight: goal._achivedCriteriaWeight, + }))} onTagClick={setTagsFilterOutside} onGoalClick={onProjectClickHandler} /> diff --git a/src/utils/kanban.ts b/src/utils/kanban.ts deleted file mode 100644 index 65a70cde1..000000000 --- a/src/utils/kanban.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { DashboardGoal } from '../../trpc/inferredTypes'; - -export const buildKanban = (goals: NonNullable[]) => { - return goals.reduce[]>>((acum, goal) => { - const stateKey = goal.stateId; - - if (!stateKey) { - return acum; - } - - if (!acum[stateKey]) { - acum[stateKey] = []; - } - - acum[stateKey].push(goal); - - return acum; - }, {}); -}; diff --git a/trpc/router/projectV2.ts b/trpc/router/projectV2.ts index f36fd397b..9aa1fa9d1 100644 --- a/trpc/router/projectV2.ts +++ b/trpc/router/projectV2.ts @@ -50,7 +50,7 @@ interface ProjectsWithGoals extends Pick { tags: Tag[] | null; owner: ProjectActivity; _achivedCriteriaWeight: number | null; - _counts: { comments: number }; + _count: { comments: number }; state: ExtractTypeFromGenerated; priority: ExtractTypeFromGenerated; partnershipProjects: Array>;