From f5dab726737a55c9c7e6b1372d0d5807d64fc54d Mon Sep 17 00:00:00 2001 From: jackrescuer-gif Date: Wed, 20 May 2026 11:24:17 +0300 Subject: [PATCH] =?UTF-8?q?fix(digest):=20default=20scope=3Dteam=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=BC=D0=B5=D0=BD=D0=B5=D0=B4=D0=B6=D0=B5=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=20+=20responsive=20layout=202-col?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Два фикса по feedback после первого MVP: 1. **Нет данных**: default scope был `personal`, фильтрует по assigneeId=me. Менеджер, у которого нет лично назначенных задач, видел нули. Сменил default на `team` для Owner/Member (для Viewer остаётся personal). Пользователь может вручную переключить — сохраняем выбор. 2. **Виджеты схлопнуты**: CSS Grid `repeat(auto-fit, minmax(280px, 1fr))` на десктопе давал 4 узких колонки по 280px высотой 320px — нечитаемо. Заменил на AntD Row/Col с явными брейкпоинтами: - xs (≤576): 1 колонка - md+ (≥768): 2 колонки (по ~560px каждая) Снизил minHeight карточек (220/180/220/280 вместо 280/200/260/320) — меньше пустого пространства внутри. --- frontend/src/pages/DigestPage.tsx | 46 +++++++++++++++++-------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/frontend/src/pages/DigestPage.tsx b/frontend/src/pages/DigestPage.tsx index 332c3fe..838b2e7 100644 --- a/frontend/src/pages/DigestPage.tsx +++ b/frontend/src/pages/DigestPage.tsx @@ -1,6 +1,6 @@ import { useEffect, useMemo, useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; -import { Radio, Button, message, Tooltip, theme } from 'antd'; +import { Radio, Button, message, Tooltip, theme, Row, Col } from 'antd'; import { ReloadOutlined } from '@ant-design/icons'; import { useWorkspaceStore } from '../store/workspace.store'; import { useDigest } from '../hooks/useDigest'; @@ -44,7 +44,7 @@ export default function DigestPage() { const navigate = useNavigate(); const { token } = theme.useToken(); const { workspaces, current, setCurrent, load } = useWorkspaceStore(); - const [scope, setScope] = useState('personal'); + const [scope, setScope] = useState(null); const [period, setPeriod] = useState('7d'); const [statusMessage, setStatusMessage] = useState(''); @@ -64,19 +64,24 @@ export default function DigestPage() { const role = foundWs?.role; const isViewer = role === 'VIEWER'; + // Default scope: team for managers (Owner/Member), personal for viewers. + // Until user manually picks, follow the role-derived default. + const defaultScope: DigestScope = isViewer ? 'personal' : 'team'; + const activeScope: DigestScope = scope ?? defaultScope; + // Derive effective scope synchronously (no useEffect-loop): viewer is always personal. - const effectiveScope: DigestScope = isViewer && scope === 'team' ? 'personal' : scope; + const effectiveScope: DigestScope = isViewer && activeScope === 'team' ? 'personal' : activeScope; const { data, loading, error, forbiddenTeam, refetch } = useDigest(workspaceId, effectiveScope, period); // Auto-switch + toast when backend returns 403 (e.g. stale role on client). useEffect(() => { - if (forbiddenTeam && scope === 'team') { + if (forbiddenTeam && activeScope === 'team') { message.warning('Командный разрез недоступен (нужна роль Owner или Member). Показываем личные метрики.'); // eslint-disable-next-line react-hooks/set-state-in-effect setScope('personal'); } - }, [forbiddenTeam, scope]); + }, [forbiddenTeam, activeScope]); // Announce data refresh for screen readers via a dedicated live region. useEffect(() => { @@ -132,7 +137,7 @@ export default function DigestPage() {

Дайджест

handleScopeChange(e.target.value as DigestScope)} aria-label="Чьи задачи показывать" size="large" @@ -227,19 +232,14 @@ export default function DigestPage() { )} -
+ + @@ -279,13 +279,14 @@ export default function DigestPage() {
)} - + + {data && ( @@ -312,13 +313,14 @@ export default function DigestPage() { )} - + + @@ -357,13 +359,14 @@ export default function DigestPage() { )} - + + @@ -385,7 +388,8 @@ export default function DigestPage() { )} - + + ); }