From 446513c4d70039563dac2452f2545732ba2e5836 Mon Sep 17 00:00:00 2001 From: Anton Sabotovich Date: Fri, 20 Sep 2024 01:22:51 +0300 Subject: [PATCH] feat: sticky layout --- package-lock.json | 128 +++- package.json | 2 +- .../DashboardPage/DashboardPage.tsx | 45 +- .../ExploreProjectsPage.tsx | 45 +- .../ExploreProjectsStarredPage.tsx | 83 +-- .../ExploreTopProjectsPage.tsx | 45 +- src/components/GoalPage/GoalPage.tsx | 104 +-- src/components/GoalsPage/GoalsPage.tsx | 19 +- src/components/Kanban/Kanban.module.css | 15 +- src/components/Kanban/Kanban.tsx | 2 +- src/components/Kanban/KanbanScroller.tsx | 10 + src/components/Page/Page.module.css | 5 +- src/components/Page/PageContentContainer.tsx | 8 + .../ProjectListItemCollapsable.module.css | 8 + .../ProjectListItemCollapsable.tsx | 9 +- .../ProjectListItemConnected.tsx | 1 + src/components/ProjectPage/ProjectPage.tsx | 39 +- .../ProjectSettingsPage.tsx | 624 +++++++++--------- .../ProjectTeamPage/ProjectTeamPage.tsx | 91 +-- .../UserSettingsPage/UserSettingsPage.tsx | 258 ++++---- src/pages/help/[slug].tsx | 27 +- 21 files changed, 895 insertions(+), 673 deletions(-) create mode 100644 src/components/Kanban/KanbanScroller.tsx create mode 100644 src/components/Page/PageContentContainer.tsx diff --git a/package-lock.json b/package-lock.json index 9f79e9904..6affe2bc5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@sentry/nextjs": "7.99.0", "@tanstack/react-query": "4.29.5", "@tanstack/react-query-devtools": "4.29.6", - "@taskany/bricks": "5.48.1", + "@taskany/bricks": "5.50.0", "@taskany/colors": "1.13.0", "@taskany/icons": "2.0.7", "@tippyjs/react": "4.2.6", @@ -8605,9 +8605,9 @@ } }, "node_modules/@taskany/bricks": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@taskany/bricks/-/bricks-5.48.1.tgz", - "integrity": "sha512-coccGy1fTxHpnJTrSlk1ar4sYUpABZWbREF5KFlJ5NBPo+Tiw93iR9qChQugfMXFtJyZ7x4/EsB/o+dv11QbGw==", + "version": "5.50.0", + "resolved": "https://registry.npmjs.org/@taskany/bricks/-/bricks-5.50.0.tgz", + "integrity": "sha512-vsk7HPtjafMPmpWwsZiyyhiVi9QlkodtYJBKWdpakZupEoa4au2tneBJyQHCO+u+176RmwWiQ1Ihm+Q1I18m/w==", "dependencies": { "@monaco-editor/react": "4.6.0", "@taskany/colors": "1.13.0", @@ -27771,6 +27771,126 @@ "type": "github", "url": "https://github.com/sponsors/wooorm" } + }, + "node_modules/react-email/node_modules/@next/swc-darwin-x64": { + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.2.tgz", + "integrity": "sha512-iZuYr7ZvGLPjPmfhhMl0ISm+z8EiyLBC1bLyFwGBxkWmPXqdJ60mzuTaDSr5WezDwv0fz32HB7JHmRC6JVHSZg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/react-email/node_modules/@next/swc-linux-arm64-gnu": { + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.2.tgz", + "integrity": "sha512-2xVabFtIge6BJTcJrW8YuUnYTuQjh4jEuRuS2mscyNVOj6zUZkom3CQg+egKOoS+zh2rrro66ffSKIS+ztFJTg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/react-email/node_modules/@next/swc-linux-arm64-musl": { + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.2.tgz", + "integrity": "sha512-wKRCQ27xCUJx5d6IivfjYGq8oVngqIhlhSAJntgXLt7Uo9sRT/3EppMHqUZRfyuNBTbykEre1s5166z+pvRB5A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/react-email/node_modules/@next/swc-linux-x64-gnu": { + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.2.tgz", + "integrity": "sha512-NpCa+UVhhuNeaFVUP1Bftm0uqtvLWq2JTm7+Ta48+2Uqj2mNXrDIvyn1DY/ZEfmW/1yvGBRaUAv9zkMkMRixQA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/react-email/node_modules/@next/swc-linux-x64-musl": { + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.2.tgz", + "integrity": "sha512-ZWVC72x0lW4aj44e3khvBrj2oSYj1bD0jESmyah3zG/3DplEy/FOtYkMzbMjHTdDSheso7zH8GIlW6CDQnKhmQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/react-email/node_modules/@next/swc-win32-arm64-msvc": { + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.2.tgz", + "integrity": "sha512-pLT+OWYpzJig5K4VKhLttlIfBcVZfr2+Xbjra0Tjs83NQSkFS+y7xx+YhCwvpEmXYLIvaggj2ONPyjbiigOvHQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/react-email/node_modules/@next/swc-win32-ia32-msvc": { + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.2.tgz", + "integrity": "sha512-dhpiksQCyGca4WY0fJyzK3FxMDFoqMb0Cn+uDB+9GYjpU2K5//UGPQlCwiK4JHxuhg8oLMag5Nf3/IPSJNG8jw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/react-email/node_modules/@next/swc-win32-x64-msvc": { + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.2.tgz", + "integrity": "sha512-O7bort1Vld00cu8g0jHZq3cbSTUNMohOEvYqsqE10+yfohhdPHzvzO+ziJRz4Dyyr/fYKREwS7gR4JC0soSOMw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } } } } diff --git a/package.json b/package.json index df2c3446f..03f387b17 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@sentry/nextjs": "7.99.0", "@tanstack/react-query": "4.29.5", "@tanstack/react-query-devtools": "4.29.6", - "@taskany/bricks": "5.48.1", + "@taskany/bricks": "5.50.0", "@taskany/colors": "1.13.0", "@taskany/icons": "2.0.7", "@tippyjs/react": "4.2.6", diff --git a/src/components/DashboardPage/DashboardPage.tsx b/src/components/DashboardPage/DashboardPage.tsx index f2959fc21..40ca089f6 100644 --- a/src/components/DashboardPage/DashboardPage.tsx +++ b/src/components/DashboardPage/DashboardPage.tsx @@ -17,13 +17,14 @@ import { LoadMoreButton } from '../LoadMoreButton/LoadMoreButton'; import { ProjectListItemConnected } from '../ProjectListItemConnected/ProjectListItemConnected'; import { PresetModals } from '../PresetModals'; import { FiltersPanel } from '../FiltersPanel/FiltersPanel'; +import { KanbanScroller } from '../Kanban/KanbanScroller'; import { tr } from './DashboardPage.i18n'; export const DashboardPage = ({ user, ssrTime, defaultPresetFallback }: ExternalPageProps) => { const { preset } = useFiltersPreset({ defaultPresetFallback }); - const { currentPreset, queryState } = useUrlFilterParams({ + const { currentPreset, queryState, view } = useUrlFilterParams({ preset, }); @@ -89,29 +90,31 @@ export const DashboardPage = ({ user, ssrTime, defaultPresetFallback }: External /> } > - - {groupsOnScreen?.map(({ ...project }, i) => ( - + + {groupsOnScreen?.map(({ ...project }, i) => ( + + ))} + + + {nullable(hasNextPage, () => ( + void} + {...dashboardLoadMore.attr} /> ))} - - - {nullable(hasNextPage, () => ( - void} - {...dashboardLoadMore.attr} - /> - ))} - + + ); }; diff --git a/src/components/ExploreProjectsPage/ExploreProjectsPage.tsx b/src/components/ExploreProjectsPage/ExploreProjectsPage.tsx index 1280ebcb9..21c409977 100644 --- a/src/components/ExploreProjectsPage/ExploreProjectsPage.tsx +++ b/src/components/ExploreProjectsPage/ExploreProjectsPage.tsx @@ -4,6 +4,7 @@ import { Table } from '@taskany/bricks/harmony'; import { ExternalPageProps } from '../../utils/declareSsrProps'; import { routes } from '../../hooks/router'; import { Page } from '../Page/Page'; +import { PageContentContainer } from '../Page/PageContentContainer'; import { ExplorePageHeader } from '../ExplorePageHeader/ExplorePageHeader'; import { ProjectListItem } from '../ProjectListItem/ProjectListItem'; import { TableRowItem, TableRowItemTitle } from '../TableRowItem/TableRowItem'; @@ -18,27 +19,29 @@ export const ExploreProjectsPage = ({ user, ssrTime }: ExternalPageProps) => { return ( }> - - {data.projects.map((project) => - nullable(project, (p) => ( - {p.title}}> - - - )), - )} -
+ + + {data.projects.map((project) => + nullable(project, (p) => ( + {p.title}}> + + + )), + )} +
+
); }; diff --git a/src/components/ExploreProjectsStarredPage/ExploreProjectsStarredPage.tsx b/src/components/ExploreProjectsStarredPage/ExploreProjectsStarredPage.tsx index 96dadff58..626b62024 100644 --- a/src/components/ExploreProjectsStarredPage/ExploreProjectsStarredPage.tsx +++ b/src/components/ExploreProjectsStarredPage/ExploreProjectsStarredPage.tsx @@ -5,6 +5,7 @@ import NextLink from 'next/link'; import { ExternalPageProps } from '../../utils/declareSsrProps'; import { routes } from '../../hooks/router'; import { Page } from '../Page/Page'; +import { PageContentContainer } from '../Page/PageContentContainer'; import { ProjectListItem } from '../ProjectListItem/ProjectListItem'; import { TableRowItem, TableRowItemTitle } from '../TableRowItem/TableRowItem'; import { trpc } from '../../utils/trpcClient'; @@ -19,47 +20,49 @@ export const ExploreProjectsStarredPage = ({ user, ssrTime }: ExternalPageProps) return ( }> - {nullable(data, (projects) => - nullable( - projects.length, - () => ( - - {projects.map((project) => - nullable(project, (p) => ( - {p.title}}> - - - )), - )} -
+ + {nullable(data, (projects) => + nullable( + projects.length, + () => ( + + {projects.map((project) => + nullable(project, (p) => ( + {p.title}}> + + + )), + )} +
+ ), + + + + {tr("You haven't starred any projects")}{' '} + {tr('Go to any project page and mark it with a star')} + + + + {tr('All projects')} + + + + , ), - - - - {tr("You haven't starred any projects")}{' '} - {tr('Go to any project page and mark it with a star')} - - - - {tr('All projects')} - - - - , - ), - )} + )} +
); }; diff --git a/src/components/ExploreTopProjectsPage/ExploreTopProjectsPage.tsx b/src/components/ExploreTopProjectsPage/ExploreTopProjectsPage.tsx index bd43c8b98..d67526b72 100644 --- a/src/components/ExploreTopProjectsPage/ExploreTopProjectsPage.tsx +++ b/src/components/ExploreTopProjectsPage/ExploreTopProjectsPage.tsx @@ -4,6 +4,7 @@ import { Table } from '@taskany/bricks/harmony'; import { ExternalPageProps } from '../../utils/declareSsrProps'; import { routes } from '../../hooks/router'; import { Page } from '../Page/Page'; +import { PageContentContainer } from '../Page/PageContentContainer'; import { ExplorePageHeader } from '../ExplorePageHeader/ExplorePageHeader'; import { ProjectListItem } from '../ProjectListItem/ProjectListItem'; import { TableRowItem, TableRowItemTitle } from '../TableRowItem/TableRowItem'; @@ -18,27 +19,29 @@ export const ExploreProjectsPage = ({ user, ssrTime }: ExternalPageProps) => { return ( }> - - {projects.data.map((project) => - nullable(project, (p) => ( - {p.title}}> - - - )), - )} -
+ + + {projects.data.map((project) => + nullable(project, (p) => ( + {p.title}}> + + + )), + )} +
+
); }; diff --git a/src/components/GoalPage/GoalPage.tsx b/src/components/GoalPage/GoalPage.tsx index 02e629cf3..87832aa4c 100644 --- a/src/components/GoalPage/GoalPage.tsx +++ b/src/components/GoalPage/GoalPage.tsx @@ -6,6 +6,7 @@ import { Button } from '@taskany/bricks/harmony'; import { ExternalPageProps } from '../../utils/declareSsrProps'; import { ModalEvent, dispatchModalEvent } from '../../utils/dispatchModal'; import { Page } from '../Page/Page'; +import { PageContentContainer } from '../Page/PageContentContainer'; import { PageActions } from '../PageActions/PageActions'; import { PageSep } from '../PageSep/PageSep'; import { IssueKey } from '../IssueKey/IssueKey'; @@ -82,58 +83,65 @@ export const GoalPage = ({ user, ssrTime, params: { id } }: ExternalPageProps<{ return ( } {...goalPage.attr}> - - - - - - -
+ + - - {nullable(goal._isEditable, () => ( -
- - } - /> - - - -
-
- - -
- -
- router.goal(transferredGoal._shortId)} - onGoalClick={onGoalClick} - /> +
+ + + {nullable(goal._isEditable, () => ( +
+ + } + /> + + + +
+
+ + + +
+ +
+ router.goal(transferredGoal._shortId)} + onGoalClick={onGoalClick} + /> +
-
+ ); }; diff --git a/src/components/GoalsPage/GoalsPage.tsx b/src/components/GoalsPage/GoalsPage.tsx index 75c6f96ee..430a667ab 100644 --- a/src/components/GoalsPage/GoalsPage.tsx +++ b/src/components/GoalsPage/GoalsPage.tsx @@ -6,6 +6,7 @@ import { trpc } from '../../utils/trpcClient'; import { useUrlFilterParams } from '../../hooks/useUrlFilterParams'; import { useFiltersPreset } from '../../hooks/useFiltersPreset'; import { Page } from '../Page/Page'; +import { PageContentContainer } from '../Page/PageContentContainer'; import { getPageTitle } from '../../utils/getPageTitle'; import { GroupedGoalList } from '../GroupedGoalList'; import { FlatGoalList } from '../FlatGoalList'; @@ -58,14 +59,16 @@ export const GoalsPage = ({ user, ssrTime, defaultPresetFallback, baseQueryState /> } > - {nullable( - groupedView, - () => ( - - ), - , - )} - + + {nullable( + groupedView, + () => ( + + ), + , + )} + + ); }; diff --git a/src/components/Kanban/Kanban.module.css b/src/components/Kanban/Kanban.module.css index 5ac032a8a..a13829eb1 100644 --- a/src/components/Kanban/Kanban.module.css +++ b/src/components/Kanban/Kanban.module.css @@ -1,5 +1,11 @@ .KanbanState{ - margin-bottom: var(--gap-m); + padding: var(--gap-m) 0; + position: sticky; + width: 100%; + top: calc(var(--gap-l) + var(--gap-s)); + background: var(--background); + background: linear-gradient(var(--background) 70%, rgba(0, 0, 0, 0) 100%); + z-index: 2; } .KanbanLink { @@ -12,4 +18,11 @@ .KanbanLink:not(:last-child) { margin-bottom: var(--gap-sm); +} + +.KanbanScroller { + margin: 0 var(--gap-sm); + padding: 0 var(--gap-s); + padding-top: calc(var(--gap-m) + var(--gap-xs)); + box-sizing: border-box; } \ No newline at end of file diff --git a/src/components/Kanban/Kanban.tsx b/src/components/Kanban/Kanban.tsx index f3e5d4816..e5f50cdef 100644 --- a/src/components/Kanban/Kanban.tsx +++ b/src/components/Kanban/Kanban.tsx @@ -133,7 +133,7 @@ const KanbanStateColumn: FC = ({ ); return ( - + {goals.map((goal) => { diff --git a/src/components/Kanban/KanbanScroller.tsx b/src/components/Kanban/KanbanScroller.tsx new file mode 100644 index 000000000..ae2f8506f --- /dev/null +++ b/src/components/Kanban/KanbanScroller.tsx @@ -0,0 +1,10 @@ +import { FC, HTMLAttributes } from 'react'; +import { KanbanScroller as KanbanScrollerBricks } from '@taskany/bricks/harmony'; + +import s from './Kanban.module.css'; + +export const KanbanScroller: FC<{ shadow?: number } & HTMLAttributes> = ({ shadow, children }) => ( + + {children} + +); diff --git a/src/components/Page/Page.module.css b/src/components/Page/Page.module.css index cf9b24b93..70accc053 100644 --- a/src/components/Page/Page.module.css +++ b/src/components/Page/Page.module.css @@ -19,6 +19,9 @@ .PageContent { flex: 1; overflow: auto; +} + +.PageContentContainer { margin: var(--gap-xs) var(--gap-sm) 0; - padding: var(--gap-m) var(--gap-s); + padding: var(--gap-m) var(--gap-s) 0; } \ No newline at end of file diff --git a/src/components/Page/PageContentContainer.tsx b/src/components/Page/PageContentContainer.tsx new file mode 100644 index 000000000..91df71c70 --- /dev/null +++ b/src/components/Page/PageContentContainer.tsx @@ -0,0 +1,8 @@ +import { FC, HTMLAttributes } from 'react'; +import cn from 'classnames'; + +import s from './Page.module.css'; + +export const PageContentContainer: FC> = ({ children, className }) => ( +
{children}
+); diff --git a/src/components/ProjectListItemCollapsable/ProjectListItemCollapsable.module.css b/src/components/ProjectListItemCollapsable/ProjectListItemCollapsable.module.css index 85b787086..ea5eba136 100644 --- a/src/components/ProjectListItemCollapsable/ProjectListItemCollapsable.module.css +++ b/src/components/ProjectListItemCollapsable/ProjectListItemCollapsable.module.css @@ -18,4 +18,12 @@ color: var(--text-ghost); font-size: inherit; font-weight: inherit; +} + +.ProjectListItemCollapsableHeader_sticky { + cursor: pointer; + position: sticky; + top: 0; + z-index: 3; + background-color: var(--background); } \ No newline at end of file diff --git a/src/components/ProjectListItemCollapsable/ProjectListItemCollapsable.tsx b/src/components/ProjectListItemCollapsable/ProjectListItemCollapsable.tsx index 72fac0844..a75c2aa47 100644 --- a/src/components/ProjectListItemCollapsable/ProjectListItemCollapsable.tsx +++ b/src/components/ProjectListItemCollapsable/ProjectListItemCollapsable.tsx @@ -1,5 +1,6 @@ import React, { ComponentProps, MouseEventHandler, ReactNode } from 'react'; import { nullable } from '@taskany/bricks'; +import cn from 'classnames'; import { TreeView, TreeViewNode, TreeViewTitle, Text } from '@taskany/bricks/harmony'; import { IconServersOutline } from '@taskany/icons'; @@ -21,6 +22,7 @@ interface ProjectListItemCollapsableProps extends Omit; titleSize?: 'm' | 'l'; actionButtonView?: 'default' | 'icons'; + sticky?: boolean; } export const ProjectListItemCollapsable: React.FC = ({ @@ -34,6 +36,7 @@ export const ProjectListItemCollapsable: React.FC { const projectComponent = ( @@ -77,7 +80,11 @@ export const ProjectListItemCollapsable: React.FC {projectComponent}} + title={ + + {projectComponent} + + } {...props} > {goals} diff --git a/src/components/ProjectListItemConnected/ProjectListItemConnected.tsx b/src/components/ProjectListItemConnected/ProjectListItemConnected.tsx index 3b5d108f6..54231d9a4 100644 --- a/src/components/ProjectListItemConnected/ProjectListItemConnected.tsx +++ b/src/components/ProjectListItemConnected/ProjectListItemConnected.tsx @@ -76,6 +76,7 @@ export const ProjectListItemConnected: FC = ({ href={routes.project(project.id, view ? `view=${view}` : undefined)} onClick={onProjectClickHandler} project={project} + sticky={isKanbanView} parent={isKanbanView ? parent : undefined} goals={ diff --git a/src/components/ProjectPage/ProjectPage.tsx b/src/components/ProjectPage/ProjectPage.tsx index 28616260d..aafc9a0a4 100644 --- a/src/components/ProjectPage/ProjectPage.tsx +++ b/src/components/ProjectPage/ProjectPage.tsx @@ -17,6 +17,7 @@ import { ProjectPageTabs } from '../ProjectPageTabs/ProjectPageTabs'; import { PresetModals } from '../PresetModals'; import { FiltersPanel } from '../FiltersPanel/FiltersPanel'; import { ProjectContext } from '../ProjectContext/ProjectContext'; +import { KanbanScroller } from '../Kanban/KanbanScroller'; import { tr } from './ProjectPage.i18n'; import s from './ProjectPage.module.css'; @@ -28,7 +29,7 @@ export const ProjectPage = ({ user, ssrTime, params: { id }, defaultPresetFallba const { preset } = useFiltersPreset({ defaultPresetFallback }); - const { queryState } = useUrlFilterParams({ + const { queryState, view } = useUrlFilterParams({ preset, }); @@ -103,25 +104,27 @@ export const ProjectPage = ({ user, ssrTime, params: { id }, defaultPresetFallba } > - {nullable(project?.parent, (p) => ( - - {p.map((item) => ( - - - {item.title} - - - ))} - - ))} - - - {nullable(project, (p) => ( - + + {nullable(project?.parent, (p) => ( + + {p.map((item) => ( + + + {item.title} + + + ))} + ))} - - + + {nullable(project, (p) => ( + + ))} + + + + ); diff --git a/src/components/ProjectSettingsPage/ProjectSettingsPage.tsx b/src/components/ProjectSettingsPage/ProjectSettingsPage.tsx index a08c66f80..9d05975c4 100644 --- a/src/components/ProjectSettingsPage/ProjectSettingsPage.tsx +++ b/src/components/ProjectSettingsPage/ProjectSettingsPage.tsx @@ -25,6 +25,7 @@ import { useRouter } from '../../hooks/router'; import { SettingsCard, SettingsContent } from '../SettingsContent/SettingsContent'; import { dispatchModalEvent, ModalEvent } from '../../utils/dispatchModal'; import { Page } from '../Page/Page'; +import { PageContentContainer } from '../Page/PageContentContainer'; import { useProjectResource } from '../../hooks/useProjectResource'; import { errorsProvider } from '../../utils/forms'; import { trpc } from '../../utils/trpcClient'; @@ -167,338 +168,345 @@ export const ProjectSettingsPage = ({ user, ssrTime, params: { id } }: ExternalP } > - - -
-
- - - {tr('key')}: - - - + + + + +
+ + + {tr('key')}: + + + + + + + {tr('Title')}: + + + {nullable(errorsResolver('title'), (error) => ( + + ))} + + + + + {tr('Description')}: + + + {nullable(errorsResolver('description'), (error) => ( + + ))} + + + + + {tr('Parent')}: + + + { + const value = field.value ?? []; + + const onProjectRemove = (project: { id: string }) => + field.onChange(value.filter((p) => p.id !== project.id) ?? []); + + const onProjectAdd = (project: { id: string }) => + field.onChange([...value, project]); + + return ( +
+ {field.value?.map((project) => ( + onProjectRemove(project)} + // eslint-disable-next-line max-len + {...projectSettingsParentMultiInputTagClean.attr} + /> + } + > + {project.title} + + ))} + id)]} + value={value} + renderTrigger={(props) => ( + + )} + /> +
+ ); + }} + /> +
+
- - - {tr('Title')}: - - - {nullable(errorsResolver('title'), (error) => ( - - ))} - + + +
+ + - - -