Skip to content

Commit

Permalink
feat(GoalPreview): added sidebar like on GoalPage
Browse files Browse the repository at this point in the history
  • Loading branch information
DenisVorop committed Oct 13, 2023
1 parent 49160d0 commit 203bef5
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 73 deletions.
7 changes: 1 addition & 6 deletions src/components/GoalPage/GoalPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { GoalContentHeader } from '../GoalContentHeader/GoalContentHeader';
import { GoalActivityFeed } from '../GoalActivityFeed';
import { IssueParent } from '../IssueParent';
import { issuePage } from '../../utils/domObjects';
import { TagObject } from '../../types/tag';

import { tr } from './GoalPage.i18n';

Expand All @@ -35,12 +36,6 @@ const GoalContent = styled(PageContent)`
gap: ${gapM};
`;

interface TagObject {
id: string;
title: string;
description?: string | null;
}

export const GoalPage = ({ user, ssrTime, params: { id } }: ExternalPageProps<{ id: string }>) => {
const router = useRouter();

Expand Down
3 changes: 1 addition & 2 deletions src/components/GoalPreview/GoalPreview.i18n/en.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
{
"Edit": "Edit",
"Delete": "Delete"
"Edit": "Edit"
}
3 changes: 1 addition & 2 deletions src/components/GoalPreview/GoalPreview.i18n/ru.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
{
"Edit": "Изменить",
"Delete": "Удалить"
"Edit": "Редактировать"
}
141 changes: 84 additions & 57 deletions src/components/GoalPreview/GoalPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React, { FC, useCallback, useMemo, useRef } from 'react';
import React, { FC, useCallback, useRef } from 'react';
import styled from 'styled-components';
import { danger0 } from '@taskany/colors';
import { Button, Dot, Dropdown, MenuItem, ModalContent, ModalHeader, ModalPreview, nullable } from '@taskany/bricks';
import { IconMoreVerticalOutline, IconBinOutline, IconEditOutline } from '@taskany/icons';
import { Button, Dot, ModalContent, ModalHeader, ModalPreview, nullable } from '@taskany/bricks';
import { IconEditOutline } from '@taskany/icons';

import { routes } from '../../hooks/router';
import { dispatchModalEvent, ModalEvent } from '../../utils/dispatchModal';
Expand All @@ -12,6 +11,8 @@ import { GoalHeader } from '../GoalHeader';
import { GoalContentHeader } from '../GoalContentHeader/GoalContentHeader';
import { GoalActivityFeed } from '../GoalActivityFeed';
import { IssueParent } from '../IssueParent';
import { GoalSidebar } from '../GoalSidebar/GoalSidebar';
import { TagObject } from '../../types/tag';

import { useGoalPreview } from './GoalPreviewProvider';
import { tr } from './GoalPreview.i18n';
Expand All @@ -24,6 +25,12 @@ interface GoalPreviewProps {
onDelete?: () => void;
}

const StyledModalWrapper = styled.div`
display: grid;
grid-template-columns: 1fr 250px;
overflow: auto;
`;

const StyledModalHeader = styled(ModalHeader)`
top: 0;
position: sticky;
Expand All @@ -32,40 +39,67 @@ const StyledModalHeader = styled(ModalHeader)`
`;

const StyledModalContent = styled(ModalContent)`
overflow: auto;
z-index: 2; // needed that dropdowns will be upper than sidebar
`;

const StyledStickyModalContent = styled(ModalContent)`
position: sticky;
top: 0;
height: fit-content;
`;

const StyledModalPreview = styled(ModalPreview)`
width: 850px;
display: flex;
flex-direction: column;
`;

const GoalPreviewModal: React.FC<GoalPreviewProps> = ({ shortId, goal, defaults, onClose, onDelete }) => {
const { setPreview } = useGoalPreview();
const onPreviewClose = useCallback(() => {
onClose?.();
}, [onClose]);

const onEditMenuChange = useCallback((item: { onClick: () => void }) => {
item.onClick?.();
}, []);
const { goalProjectChange, onGoalStateChange, goalTagsUpdate, invalidate } = useGoalResource(
{ id: goal?.id },
{ invalidate: { getById: shortId } },
);

// FIXME https://github.com/taskany-inc/issues/issues/1853
const onGoalTagAdd = useCallback(
async (value: TagObject[]) => {
if (!goal) return;

await goalTagsUpdate([...goal.tags, ...value]);

const { onGoalStateChange } = useGoalResource({ id: goal?.id }, { invalidate: { getById: shortId } });

const goalEditMenuItems = useMemo(
() => [
{
label: tr('Edit'),
icon: <IconEditOutline size="xxs" />,
onClick: dispatchModalEvent(ModalEvent.GoalEditModal),
},
{
label: tr('Delete'),
color: danger0,
icon: <IconBinOutline size="xxs" />,
onClick: dispatchModalEvent(ModalEvent.GoalDeleteModal),
},
],
[],
invalidate();
},
[goal, invalidate, goalTagsUpdate],
);

const onGoalTagRemove = useCallback(
(value: TagObject) => async () => {
if (!goal) return;

const tags = goal.tags.filter((tag) => tag.id !== value.id);
await goalTagsUpdate(tags);

invalidate();
},
[goal, invalidate, goalTagsUpdate],
);

const onGoalTransfer = useCallback(
async (project?: { id: string }) => {
if (!project) return;

const transferedGoal = await goalProjectChange(project.id);

if (transferedGoal) {
setPreview(transferedGoal._shortId, transferedGoal);
}
},
[goalProjectChange, setPreview],
);

const commentsRef = useRef<HTMLDivElement>(null);
Expand All @@ -86,29 +120,10 @@ const GoalPreviewModal: React.FC<GoalPreviewProps> = ({ shortId, goal, defaults,
actions={nullable(goal?._isEditable, () => (
<>
<div />
<Dropdown
onChange={onEditMenuChange}
items={goalEditMenuItems}
renderTrigger={({ ref, onClick }) => (
<Button
ref={ref}
ghost
iconLeft={<IconMoreVerticalOutline size="xs" />}
onClick={onClick}
/>
)}
renderItem={({ item, cursor, index, onClick }) => (
<MenuItem
key={item.label}
ghost
color={item.color}
focused={cursor === index}
icon={item.icon}
onClick={onClick}
>
{item.label}
</MenuItem>
)}
<Button
text={tr('Edit')}
iconLeft={<IconEditOutline size="xs" />}
onClick={dispatchModalEvent(ModalEvent.GoalEditModal)}
/>
</>
))}
Expand All @@ -126,15 +141,27 @@ const GoalPreviewModal: React.FC<GoalPreviewProps> = ({ shortId, goal, defaults,
))}
</GoalHeader>
</StyledModalHeader>
<StyledModalContent>
{nullable(goal, (g) => (
<GoalContentHeader date={g.createdAt} description={g.description} />
))}

{nullable(goal, (g) => (
<GoalActivityFeed ref={commentsRef} goal={g} shortId={shortId} onGoalDeleteConfirm={onDelete} />
))}
</StyledModalContent>
<StyledModalWrapper>
<StyledModalContent>
{nullable(goal, (g) => (
<GoalContentHeader date={g.createdAt} description={g.description} />
))}

{nullable(goal, (g) => (
<GoalActivityFeed ref={commentsRef} goal={g} shortId={shortId} onGoalDeleteConfirm={onDelete} />
))}
</StyledModalContent>
<StyledStickyModalContent>
{nullable(goal, (g) => (
<GoalSidebar
goal={g}
onGoalTagRemove={onGoalTagRemove}
onGoalTagAdd={onGoalTagAdd}
onGoalTransfer={onGoalTransfer}
/>
))}
</StyledStickyModalContent>
</StyledModalWrapper>
</StyledModalPreview>
);
};
Expand Down
7 changes: 1 addition & 6 deletions src/components/TagComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,10 @@ import { IconTagOutline } from '@taskany/icons';
import { trpc } from '../utils/trpcClient';
import { notifyPromise } from '../utils/notifyPromise';
import { tagsCombobox } from '../utils/domObjects';
import { TagObject } from '../types/tag';

import { CommonCombobox } from './CommonCombobox';

interface TagObject {
id: string;
title: string;
description?: string | null;
}

interface TagComboBoxProps {
text?: React.ComponentProps<typeof Button>['text'];
query?: string;
Expand Down
5 changes: 5 additions & 0 deletions src/types/tag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface TagObject {
id: string;
title: string;
description?: string | null;
}

0 comments on commit 203bef5

Please sign in to comment.