diff --git a/cypress/fixtures/langs.json b/cypress/fixtures/langs.json index fd0b2b4be..ec9a117d6 100644 --- a/cypress/fixtures/langs.json +++ b/cypress/fixtures/langs.json @@ -881,6 +881,12 @@ "en": "Create" } }, + "NoGoalsText": { + "No goals yet": { + "ru": "", + "en": "" + } + }, "OfflineBanner": { "You are currently offline. Check connection.": { "ru": "Вы сейчас не в сети. Проверьте подключение.", @@ -1047,12 +1053,6 @@ "en": "Create" } }, - "ProjectListItem": { - "Create Goal": { - "ru": "", - "en": "" - } - }, "ProjectPage": { "title": { "ru": "Taskany — {project}", @@ -1209,6 +1209,12 @@ "en": "Ok, got it" } }, + "ProjectSubscriptionButtons": { + "Create goal": { + "ru": "Создать цель", + "en": "" + } + }, "ProjectSwitchPublicConfirmModal": { "You are going to make project public": { "ru": "Вы собираетесь сделать проект публичным", diff --git a/package-lock.json b/package-lock.json index 1d356c3c7..d14bd8ef8 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.43.0", + "@taskany/bricks": "5.44.0", "@taskany/colors": "1.13.0", "@taskany/icons": "2.0.7", "@tippyjs/react": "4.2.6", @@ -8557,9 +8557,9 @@ } }, "node_modules/@taskany/bricks": { - "version": "5.43.0", - "resolved": "https://registry.npmjs.org/@taskany/bricks/-/bricks-5.43.0.tgz", - "integrity": "sha512-dQV5ALRppZixCHQpBUNcg8qROvLn5nor9HMIImCcfEGsYlJcuEQBGZga5bSEmhFAX8s+jDO6Nishz+VVG4ABrA==", + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/@taskany/bricks/-/bricks-5.44.0.tgz", + "integrity": "sha512-EG8Yj1kA3QG9rm0d6iik3M9h+q51q3eFzYeVw9AQLb23wWrpnVRzb7+cBQnwNx9+3aFfrWHk/9DpsGVgKyhsyw==", "dependencies": { "@monaco-editor/react": "4.6.0", "@taskany/colors": "1.13.0", @@ -27607,126 +27607,6 @@ "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 98a17b33b..6cee5106f 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.43.0", + "@taskany/bricks": "5.44.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 a9ad7bfed..b14d23bf0 100644 --- a/src/components/DashboardPage/DashboardPage.tsx +++ b/src/components/DashboardPage/DashboardPage.tsx @@ -14,13 +14,13 @@ import { Page } from '../Page/Page'; import { useGoalPreview } from '../GoalPreview/GoalPreviewProvider'; import { useFMPMetric } from '../../utils/telemetry'; import { LoadMoreButton } from '../LoadMoreButton/LoadMoreButton'; -import { InlineCreateGoalControl } from '../InlineCreateGoalControl/InlineCreateGoalControl'; import { ProjectListItemCollapsable } from '../ProjectListItemCollapsable/ProjectListItemCollapsable'; import { routes } from '../../hooks/router'; import { GoalTableList, mapToRenderProps } from '../GoalTableList/GoalTableList'; import { PresetModals } from '../PresetModals'; import { FiltersPanel } from '../FiltersPanel/FiltersPanel'; import { Kanban, buildKanban } from '../Kanban/Kanban'; +import { NoGoalsText } from '../NoGoalsText/NoGoalsText'; import { safeUserData } from '../../utils/getUserName'; import { tr } from './DashboardPage.i18n'; @@ -151,10 +151,11 @@ export const DashboardPage = ({ user, ssrTime, defaultPresetFallback }: External project={project} href={routes.project(project.id, view ? `view=${view}` : undefined)} goals={children} - canCreateGoal + actionButtonView="icons" + editable > {nullable(!goals?.length, () => ( - + ))} ); diff --git a/src/components/NoGoalsText/NoGoalsText.i18n/en.json b/src/components/NoGoalsText/NoGoalsText.i18n/en.json new file mode 100644 index 000000000..c6be54e8f --- /dev/null +++ b/src/components/NoGoalsText/NoGoalsText.i18n/en.json @@ -0,0 +1,3 @@ +{ + "No goals yet": "" +} diff --git a/src/components/NoGoalsText/NoGoalsText.i18n/index.ts b/src/components/NoGoalsText/NoGoalsText.i18n/index.ts new file mode 100644 index 000000000..5c148475b --- /dev/null +++ b/src/components/NoGoalsText/NoGoalsText.i18n/index.ts @@ -0,0 +1,17 @@ +/* eslint-disable */ +// Do not edit, use generator to update +import { i18n, fmt, I18nLangSet } from 'easy-typed-intl'; +import getLang from '../../../utils/getLang'; + +import ru from './ru.json'; +import en from './en.json'; + +export type I18nKey = keyof typeof ru & keyof typeof en; +type I18nLang = 'ru' | 'en'; + +const keyset: I18nLangSet = {}; + +keyset['ru'] = ru; +keyset['en'] = en; + +export const tr = i18n(keyset, fmt, getLang); diff --git a/src/components/NoGoalsText/NoGoalsText.i18n/ru.json b/src/components/NoGoalsText/NoGoalsText.i18n/ru.json new file mode 100644 index 000000000..9aae75893 --- /dev/null +++ b/src/components/NoGoalsText/NoGoalsText.i18n/ru.json @@ -0,0 +1,3 @@ +{ + "No goals yet": "Пока нет целей" +} diff --git a/src/components/NoGoalsText/NoGoalsText.tsx b/src/components/NoGoalsText/NoGoalsText.tsx new file mode 100644 index 000000000..baac6bc57 --- /dev/null +++ b/src/components/NoGoalsText/NoGoalsText.tsx @@ -0,0 +1,7 @@ +import { Text } from '@taskany/bricks/harmony'; + +import { tr } from './NoGoalsText.i18n'; + +export const NoGoalsText = () => { + return {tr('No goals yet')}; +}; diff --git a/src/components/ProjectListItem/ProjectListItem.tsx b/src/components/ProjectListItem/ProjectListItem.tsx index f73fb2c6f..6c796be5b 100644 --- a/src/components/ProjectListItem/ProjectListItem.tsx +++ b/src/components/ProjectListItem/ProjectListItem.tsx @@ -4,9 +4,8 @@ import cn from 'classnames'; import { IconStarSolid, IconEyeOutline } from '@taskany/icons'; import { ComponentProps, useMemo } from 'react'; -import { ProjectSubscriptionIconButtons } from '../ProjectSubscriptionIconButtons/ProjectSubscriptionIconButtons'; import { ActivityByIdReturnType } from '../../../trpc/inferredTypes'; -import { ProjectSubscriptionButtons } from '../ProjectSubscriptionButtons'; +import { ProjectSubscriptionButtons } from '../ProjectSubscriptionButtons/ProjectSubscriptionButtons'; import { safeUserData } from '../../utils/getUserName'; import { watch, participants as participantsDO } from '../../utils/domObjects'; @@ -23,7 +22,7 @@ interface ProjectListItemProps { starred?: boolean; watching?: boolean; averageScore: number | null; - canCreateGoal?: boolean; + actionButtonView?: 'default' | 'icons'; } export const ProjectListItem: React.FC> = ({ @@ -38,7 +37,7 @@ export const ProjectListItem: React.FC { const ownerUserGroup = useMemo(() => [owner].map(safeUserData).filter(Boolean), [owner]); @@ -66,10 +65,10 @@ export const ProjectListItem: React.FC - {nullable(!canCreateGoal && !editable, () => ( + {nullable(!editable, () => ( <> {nullable(starred, () => ( @@ -81,20 +80,13 @@ export const ProjectListItem: React.FC ( ))} - - {nullable(canCreateGoal, () => ( - - ))} ); diff --git a/src/components/ProjectListItemCollapsable/ProjectListItemCollapsable.tsx b/src/components/ProjectListItemCollapsable/ProjectListItemCollapsable.tsx index 13aa962fb..0f286b2fa 100644 --- a/src/components/ProjectListItemCollapsable/ProjectListItemCollapsable.tsx +++ b/src/components/ProjectListItemCollapsable/ProjectListItemCollapsable.tsx @@ -20,7 +20,7 @@ interface ProjectListItemCollapsableProps extends Omit; titleSize?: 'm' | 'l'; editable?: boolean; - canCreateGoal?: boolean; + actionButtonView?: 'default' | 'icons'; } export const ProjectListItemCollapsable: React.FC = ({ @@ -33,8 +33,8 @@ export const ProjectListItemCollapsable: React.FC { const projectComponent = ( @@ -68,7 +68,7 @@ export const ProjectListItemCollapsable: React.FC ); diff --git a/src/components/ProjectListItemConnected.tsx b/src/components/ProjectListItemConnected.tsx index 51c999c51..7da7994d2 100644 --- a/src/components/ProjectListItemConnected.tsx +++ b/src/components/ProjectListItemConnected.tsx @@ -11,9 +11,9 @@ import { safeUserData } from '../utils/getUserName'; import { GoalTableList, mapToRenderProps } from './GoalTableList/GoalTableList'; import { ProjectListItemCollapsable } from './ProjectListItemCollapsable/ProjectListItemCollapsable'; -import { InlineCreateGoalControl } from './InlineCreateGoalControl/InlineCreateGoalControl'; import { useGoalPreview } from './GoalPreview/GoalPreviewProvider'; import { Kanban, buildKanban } from './Kanban/Kanban'; +import { NoGoalsText } from './NoGoalsText/NoGoalsText'; interface ProjectListItemConnectedProps extends ComponentProps { parent?: ComponentProps['project']; @@ -133,10 +133,7 @@ export const ProjectListItemConnected: FC = ({ {...props} > - {nullable( - !projectDeepInfo?.goals.length, - () => !isLoading && , - )} + {nullable(!projectDeepInfo?.goals.length, () => !isLoading && )} {nullable(view !== 'kanban', () => subNodes)} diff --git a/src/components/ProjectSubscriptionButtons.tsx b/src/components/ProjectSubscriptionButtons.tsx deleted file mode 100644 index 81f8e1a41..000000000 --- a/src/components/ProjectSubscriptionButtons.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { FC } from 'react'; - -import { useProjectResource } from '../hooks/useProjectResource'; - -import { StarButton } from './StarButton/StarButton'; -import { WatchButton } from './WatchButton/WatchButton'; - -interface ProjectSubscriptionButtonsProps { - id: string; - starred?: boolean; - watching?: boolean; - stargizersCounter: number; -} - -export const ProjectSubscriptionButtons: FC = ({ - id, - starred, - watching, - stargizersCounter, -}) => { - const { toggleProjectWatching, toggleProjectStar } = useProjectResource(id); - - return ( - <> - - - - ); -}; diff --git a/src/components/ProjectSubscriptionButtons/ProjectSubscriptionButtons.i18n/en.json b/src/components/ProjectSubscriptionButtons/ProjectSubscriptionButtons.i18n/en.json new file mode 100644 index 000000000..d0bb41a82 --- /dev/null +++ b/src/components/ProjectSubscriptionButtons/ProjectSubscriptionButtons.i18n/en.json @@ -0,0 +1,3 @@ +{ + "Create goal": "" +} diff --git a/src/components/ProjectSubscriptionButtons/ProjectSubscriptionButtons.i18n/index.ts b/src/components/ProjectSubscriptionButtons/ProjectSubscriptionButtons.i18n/index.ts new file mode 100644 index 000000000..5c148475b --- /dev/null +++ b/src/components/ProjectSubscriptionButtons/ProjectSubscriptionButtons.i18n/index.ts @@ -0,0 +1,17 @@ +/* eslint-disable */ +// Do not edit, use generator to update +import { i18n, fmt, I18nLangSet } from 'easy-typed-intl'; +import getLang from '../../../utils/getLang'; + +import ru from './ru.json'; +import en from './en.json'; + +export type I18nKey = keyof typeof ru & keyof typeof en; +type I18nLang = 'ru' | 'en'; + +const keyset: I18nLangSet = {}; + +keyset['ru'] = ru; +keyset['en'] = en; + +export const tr = i18n(keyset, fmt, getLang); diff --git a/src/components/ProjectSubscriptionButtons/ProjectSubscriptionButtons.i18n/ru.json b/src/components/ProjectSubscriptionButtons/ProjectSubscriptionButtons.i18n/ru.json new file mode 100644 index 000000000..c74a3f70e --- /dev/null +++ b/src/components/ProjectSubscriptionButtons/ProjectSubscriptionButtons.i18n/ru.json @@ -0,0 +1,3 @@ +{ + "Create goal": "Создать цель" +} diff --git a/src/components/ProjectSubscriptionButtons/ProjectSubscriptionButtons.tsx b/src/components/ProjectSubscriptionButtons/ProjectSubscriptionButtons.tsx new file mode 100644 index 000000000..c282e4d76 --- /dev/null +++ b/src/components/ProjectSubscriptionButtons/ProjectSubscriptionButtons.tsx @@ -0,0 +1,52 @@ +import { FC, MouseEvent } from 'react'; +import { Button } from '@taskany/bricks/harmony'; +import { IconAddOutline } from '@taskany/icons'; +import { nullable } from '@taskany/bricks'; + +import { useProjectResource } from '../../hooks/useProjectResource'; +import { dispatchModalEvent, ModalEvent } from '../../utils/dispatchModal'; +import { StarButton } from '../StarButton/StarButton'; +import { WatchButton } from '../WatchButton/WatchButton'; + +import { tr } from './ProjectSubscriptionButtons.i18n'; + +interface ProjectSubscriptionButtonsProps { + project: { + flowId: string; + title: string; + id: string; + }; + starred?: boolean; + watching?: boolean; + stargizersCounter: number; + view?: 'default' | 'icons'; +} + +export const ProjectSubscriptionButtons: FC = ({ + project, + starred, + watching, + stargizersCounter, + view = 'default', +}) => { + const { toggleProjectWatching, toggleProjectStar } = useProjectResource(project.id); + const onAddClick = (e: MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + dispatchModalEvent(ModalEvent.GoalCreateModal, { project })(); + }; + + return ( + <> + + + {nullable( + view === 'icons', + () => ( + - - - - ); -}; diff --git a/src/components/StarButton/StarButton.tsx b/src/components/StarButton/StarButton.tsx index 9d58edfd2..b4a47dc02 100644 --- a/src/components/StarButton/StarButton.tsx +++ b/src/components/StarButton/StarButton.tsx @@ -1,4 +1,4 @@ -import React, { MouseEvent, useCallback } from 'react'; +import React, { MouseEvent, useCallback, useState } from 'react'; import { nullable } from '@taskany/bricks'; import { IconStarOutline, IconStarSolid } from '@taskany/icons'; import { Button, Counter } from '@taskany/bricks/harmony'; @@ -10,33 +10,40 @@ interface StarButtonProps { count?: number; onToggle: (val: StarButtonProps['stargizer']) => void; + view?: 'default' | 'icons'; } interface IconProps { filled: boolean; } -export const Icon: React.FC = ({ filled }) => { +const Icon: React.FC = ({ filled }) => { const Comp = filled ? IconStarSolid : IconStarOutline; return ; }; -export const StarButton: React.FC = ({ stargizer, count, onToggle }) => { +export const StarButton: React.FC = ({ stargizer, count, onToggle, view = 'default' }) => { + const [isStargizer, setIsStargizer] = useState(!!stargizer); const onClick = useCallback( (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); - onToggle(stargizer); + onToggle(isStargizer); + setIsStargizer((prev) => !prev); }, - [onToggle, stargizer], + [onToggle, isStargizer], ); + if (view === 'icons') { + return