Skip to content

Commit 25e5666

Browse files
committed
refactor: moved team group, added animation, fixed ui, removed "createTeamDialog" from the team switcher, and moved static links to config
1 parent 7f7d2cc commit 25e5666

9 files changed

Lines changed: 155 additions & 82 deletions

File tree

src/app/styles/animations.scss

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,31 @@
6666
}
6767
}
6868
}
69+
70+
.collapsible-content {
71+
overflow: hidden;
72+
}
73+
.collapsible-content[data-state='open'] {
74+
animation: slideDown 300ms ease-out;
75+
}
76+
.collapsible-content[data-state='closed'] {
77+
animation: slideUp 300ms ease-out;
78+
}
79+
80+
@keyframes slideDown {
81+
from {
82+
height: 0;
83+
}
84+
to {
85+
height: var(--radix-collapsible-content-height);
86+
}
87+
}
88+
89+
@keyframes slideUp {
90+
from {
91+
height: var(--radix-collapsible-content-height);
92+
}
93+
to {
94+
height: 0;
95+
}
96+
}

src/shared/lib/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export { useQueuedDebouncedMutation } from './useQueuedDebouncedMutation';
44
export { useLocalStorageDraft, type DraftWithTTL } from './useLocalStorageDraft';
55
export { useTimer, type UseTimerOptions, type UseTimerReturn } from './use-timer/useTimer';
66
export { useZodValidationWithAsyncCheck } from './useZodValidationWithAsyncCheck';
7+
export { useIsActive } from './useIsActive';

src/shared/ui/sidebar/Sidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,7 @@ function SidebarMenuSubButton({
629629
data-size={size}
630630
data-active={isActive}
631631
className={cn(
632-
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden group-data-[collapsible=icon]:hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[size=md]:text-sm data-[size=sm]:text-xs [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
632+
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden group-data-[collapsible=icon]:hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[size=md]:text-sm data-[size=sm]:text-xs [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
633633
className
634634
)}
635635
{...props}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Mail, Settings, ShieldUser, UsersRound } from 'lucide-react';
2+
import { routes } from 'shared/config';
3+
4+
export const team = [
5+
{
6+
url: routes.team.members(),
7+
title: 'Участники',
8+
icon: UsersRound,
9+
},
10+
{ url: routes.team.invitations(), title: 'Приглашения', icon: Mail },
11+
{ url: routes.team.roles(), title: 'Роли', icon: ShieldUser },
12+
{ url: routes.team.settings(), title: 'Настройки', icon: Settings },
13+
] as const;

src/widgets/app-sidebar/ui/AppSidebar.tsx

Lines changed: 4 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,16 @@
1-
'use client';
2-
3-
import { InviteTeamMemberDialog } from 'features/teams/invite';
4-
import { ChevronRight, SquarePlusIcon, UserRound, UsersRound } from 'lucide-react';
5-
import Link from 'next/link';
6-
import { routes } from 'shared/config';
71
import {
8-
Collapsible,
9-
CollapsibleContent,
10-
CollapsibleTrigger,
112
Separator,
123
Sidebar,
134
SidebarContent,
14-
SidebarFooter,
155
SidebarGroup,
166
SidebarHeader,
177
SidebarMenu,
18-
SidebarMenuAction,
19-
SidebarMenuButton,
20-
SidebarMenuItem,
21-
SidebarMenuSub,
22-
SidebarMenuSubButton,
23-
SidebarMenuSubItem,
248
SidebarRail,
259
} from 'shared/ui';
26-
import { NavUser } from './NavUser';
2710
import { TeamsDropdown } from './teams/TeamsDropdown';
2811
import { Projects } from './Projects';
29-
30-
const team = [
31-
{
32-
url: routes.team.members(),
33-
title: 'Участники',
34-
action: (
35-
<InviteTeamMemberDialog asChild>
36-
<SquarePlusIcon />
37-
</InviteTeamMemberDialog>
38-
),
39-
},
40-
{ url: routes.team.invitations(), title: 'Приглашения', action: null },
41-
{ url: routes.team.roles(), title: 'Роли', action: null },
42-
{ url: routes.team.settings(), title: 'Настройки', action: null },
43-
];
12+
import { MyTeams } from './MyTeams';
13+
import { Team } from './Team';
4414

4515
export function AppSidebar({ ...props }: Omit<React.ComponentProps<typeof Sidebar>, 'children'>) {
4616
return (
@@ -51,49 +21,13 @@ export function AppSidebar({ ...props }: Omit<React.ComponentProps<typeof Sideba
5121
<SidebarContent className="gap-2">
5222
<SidebarGroup>
5323
<SidebarMenu>
54-
<SidebarMenuItem>
55-
<SidebarMenuButton asChild>
56-
<Link href={routes.profile.root()}>
57-
<UserRound />
58-
<span>Мой профиль</span>
59-
</Link>
60-
</SidebarMenuButton>
61-
</SidebarMenuItem>
62-
<Collapsible asChild defaultOpen className="group/collapsible">
63-
<SidebarMenuItem>
64-
<CollapsibleTrigger asChild>
65-
<SidebarMenuButton tooltip="Управление командой">
66-
<UsersRound />
67-
<span>Команда</span>
68-
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
69-
</SidebarMenuButton>
70-
</CollapsibleTrigger>
71-
<CollapsibleContent>
72-
<SidebarMenuSub>
73-
{team.map((subItem) => (
74-
<SidebarMenuSubItem key={subItem.title}>
75-
<SidebarMenuSubButton asChild>
76-
<a href={subItem.url}>
77-
<span>{subItem.title}</span>
78-
</a>
79-
</SidebarMenuSubButton>
80-
{subItem.action ? (
81-
<SidebarMenuAction>{subItem.action}</SidebarMenuAction>
82-
) : null}
83-
</SidebarMenuSubItem>
84-
))}
85-
</SidebarMenuSub>
86-
</CollapsibleContent>
87-
</SidebarMenuItem>
88-
</Collapsible>
24+
<MyTeams />
25+
<Team />
8926
</SidebarMenu>
9027
</SidebarGroup>
9128
<Separator />
9229
<Projects />
9330
</SidebarContent>
94-
<SidebarFooter>
95-
<NavUser />
96-
</SidebarFooter>
9731
<SidebarRail />
9832
</Sidebar>
9933
);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
'use client';
2+
import { UserRound } from 'lucide-react';
3+
import Link from 'next/link';
4+
import { usePathname } from 'next/navigation';
5+
import { routes } from 'shared/config';
6+
import { SidebarMenuButton, SidebarMenuItem } from 'shared/ui';
7+
8+
export function MyTeams() {
9+
const pathname = usePathname();
10+
return (
11+
<SidebarMenuItem>
12+
<SidebarMenuButton isActive={pathname === routes.profile.teams()} asChild>
13+
<Link href={routes.profile.teams()}>
14+
<UserRound />
15+
<span>Мои команды</span>
16+
</Link>
17+
</SidebarMenuButton>
18+
</SidebarMenuItem>
19+
);
20+
}

src/widgets/app-sidebar/ui/Projects.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export function Projects() {
3030
const { isMobile } = useSidebar();
3131
const [createProjectOpen, setCreateProjectOpen] = useState(false);
3232
const projects = useQuery({ ...ProjectQueries.getProjects(slug!), enabled: !!slug });
33+
const projectList = projects.data?.items.slice(0, 6) ?? [];
34+
const showOtherProjectsButton = projectList.length > 6;
3335

3436
if (!projects.data) {
3537
return null;
@@ -47,7 +49,7 @@ export function Projects() {
4749
<Plus />
4850
</SidebarGroupAction>
4951
<SidebarMenu>
50-
{projects.data.items.map((project) => {
52+
{projectList.map((project) => {
5153
const canManage = Boolean(slug && project.canEdit);
5254

5355
return (
@@ -116,14 +118,16 @@ export function Projects() {
116118
</SidebarMenuItem>
117119
);
118120
})}
119-
<SidebarMenuItem>
120-
<Link href={routes.team.projects()}>
121-
<SidebarMenuButton>
122-
<MoreHorizontal />
123-
<span>Больше</span>
124-
</SidebarMenuButton>
125-
</Link>
126-
</SidebarMenuItem>
121+
{showOtherProjectsButton && (
122+
<SidebarMenuItem>
123+
<Link href={routes.team.projects()}>
124+
<SidebarMenuButton>
125+
<MoreHorizontal />
126+
<span>Больше</span>
127+
</SidebarMenuButton>
128+
</Link>
129+
</SidebarMenuItem>
130+
)}
127131
</SidebarMenu>
128132
</SidebarGroup>
129133
<CreateProjectDialog
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
'use client';
2+
import { InviteTeamMemberDialog } from 'features/teams/invite';
3+
import { UsersRound, ChevronRight } from 'lucide-react';
4+
import Link from 'next/link';
5+
import { usePathname } from 'next/navigation';
6+
import { routes } from 'shared/config';
7+
import {
8+
Collapsible,
9+
CollapsibleContent,
10+
CollapsibleTrigger,
11+
SidebarMenuButton,
12+
SidebarMenuItem,
13+
SidebarMenuSub,
14+
SidebarMenuSubButton,
15+
SidebarMenuSubItem,
16+
useSidebar,
17+
} from 'shared/ui';
18+
import { team } from '../config/sidebar';
19+
import { useRouter } from 'next/navigation';
20+
21+
export function Team() {
22+
const pathname = usePathname();
23+
const router = useRouter();
24+
const { open, isMobile } = useSidebar();
25+
26+
const isAllowedToHighlight = !open && !isMobile;
27+
28+
const handleClickTrigger = () => {
29+
if (isAllowedToHighlight) {
30+
router.push(routes.team.members());
31+
}
32+
};
33+
return (
34+
<Collapsible asChild className="group/collapsible">
35+
<SidebarMenuItem>
36+
<CollapsibleTrigger asChild>
37+
<SidebarMenuButton
38+
onClick={handleClickTrigger}
39+
isActive={isAllowedToHighlight && pathname?.startsWith(routes.team.root())}
40+
tooltip="Управление командой"
41+
>
42+
<UsersRound />
43+
Команда
44+
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
45+
</SidebarMenuButton>
46+
</CollapsibleTrigger>
47+
<CollapsibleContent className="collapsible-content">
48+
<SidebarMenuSub>
49+
{team.map((subItem) => (
50+
<SidebarMenuSubItem key={subItem.url}>
51+
<SidebarMenuSubButton isActive={pathname?.startsWith(subItem.url)} asChild>
52+
<Link href={subItem.url}>
53+
<subItem.icon />
54+
{subItem.title}
55+
</Link>
56+
</SidebarMenuSubButton>
57+
</SidebarMenuSubItem>
58+
))}
59+
60+
<SidebarMenuSubItem>
61+
<SidebarMenuSubButton asChild>
62+
<InviteTeamMemberDialog>+ Пригласить участника</InviteTeamMemberDialog>
63+
</SidebarMenuSubButton>
64+
</SidebarMenuSubItem>
65+
</SidebarMenuSub>
66+
</CollapsibleContent>
67+
</SidebarMenuItem>
68+
</Collapsible>
69+
);
70+
}

src/widgets/app-sidebar/ui/teams/TeamsDropdown.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
import { CreateTeamDialog } from 'features/teams/create';
13
import Link from 'next/link';
24
import { routes } from 'shared/config';
35
import {
@@ -12,11 +14,12 @@ import {
1214
import { useTeamsDropdown } from '../../model/useTeamsDropdown';
1315
import { TeamItem } from './TeamItem';
1416
import { TeamTrigger } from './TeamTrigger';
17+
import { useIsMobile } from 'shared/lib/hooks';
1518

1619
export function TeamsDropdown() {
1720
const { open, setOpen, query, visibleTeams, teams, hasMoreTeams, switchTeam } =
1821
useTeamsDropdown();
19-
22+
const isMobile = useIsMobile();
2023
return (
2124
<>
2225
<DropdownMenu open={open} onOpenChange={setOpen}>
@@ -32,7 +35,7 @@ export function TeamsDropdown() {
3235
<DropdownMenuContent
3336
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
3437
align="start"
35-
side={'bottom'}
38+
side={isMobile ? 'bottom' : 'right'}
3639
sideOffset={4}
3740
>
3841
<DropdownMenuLabel className="text-muted-foreground text-xs">Команды</DropdownMenuLabel>

0 commit comments

Comments
 (0)