diff --git a/src/app/layouts/SidebarLayout.tsx b/src/app/layouts/SidebarLayout.tsx index aca4604..6291ce5 100644 --- a/src/app/layouts/SidebarLayout.tsx +++ b/src/app/layouts/SidebarLayout.tsx @@ -1,7 +1,10 @@ -import { ComponentProps } from 'react'; import { TeamSlugSync } from 'features/teams/active-team'; +import { ComponentProps } from 'react'; import { Separator, SidebarInset, SidebarProvider, SidebarTrigger } from 'shared/ui'; import { AppSidebar } from 'widgets/app-sidebar'; +import { NavUser } from 'widgets/nav-user'; +import { Notifications } from 'widgets/notifications'; +import { QuickCreate } from 'widgets/quick-create'; export function SidebarLayout({ children, ...props }: ComponentProps) { return ( @@ -9,12 +12,19 @@ export function SidebarLayout({ children, ...props }: ComponentProps -
- - +
+
+ + +
+
+ + + +
{children}
diff --git a/src/shared/ui/Item.tsx b/src/shared/ui/Item.tsx index b5c4460..fc46764 100644 --- a/src/shared/ui/Item.tsx +++ b/src/shared/ui/Item.tsx @@ -136,7 +136,7 @@ function ItemDescription({ className, ...props }: React.ComponentProps<'p'>) {

a:hover]:text-primary line-clamp-2 text-left text-sm leading-normal font-normal group-data-[size=xs]/item:text-xs [&>a]:underline [&>a]:underline-offset-4', + 'text-muted-foreground [&>a:hover]:text-primary line-clamp-2 text-left text-xs leading-normal font-normal group-data-[size=xs]/item:text-xs [&>a]:underline [&>a]:underline-offset-4', className )} {...props} diff --git a/src/widgets/nav-user/index.ts b/src/widgets/nav-user/index.ts new file mode 100644 index 0000000..918613b --- /dev/null +++ b/src/widgets/nav-user/index.ts @@ -0,0 +1 @@ +export { NavUser } from './ui/NavUser'; diff --git a/src/widgets/nav-user/ui/NavUser.tsx b/src/widgets/nav-user/ui/NavUser.tsx new file mode 100644 index 0000000..c97928d --- /dev/null +++ b/src/widgets/nav-user/ui/NavUser.tsx @@ -0,0 +1,13 @@ +'use client'; + +import dynamic from 'next/dynamic'; +import { NavUserFallback } from './NavUserFallback'; + +const NavUserContent = dynamic(() => import('./NavUserContent').then((mod) => mod.NavUserContent), { + ssr: false, + loading: () => , +}); + +export function NavUser() { + return ; +} diff --git a/src/widgets/nav-user/ui/NavUserContent.tsx b/src/widgets/nav-user/ui/NavUserContent.tsx new file mode 100644 index 0000000..c4601b2 --- /dev/null +++ b/src/widgets/nav-user/ui/NavUserContent.tsx @@ -0,0 +1,77 @@ +'use client'; + +import { useSuspenseQuery } from '@tanstack/react-query'; +import { UserAvatar, UserQueries } from 'entities/user'; +import { SignOut } from 'features/auth/sign-out'; +import { LogOut, UserRoundIcon } from 'lucide-react'; +import Link from 'next/link'; +import { routes } from 'shared/config'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, + SidebarMenuButton, +} from 'shared/ui'; + +export function NavUserContent() { + const query = useSuspenseQuery(UserQueries.getMe()); + + const { + email, + profile: { avatar, firstName, lastName }, + } = query.data; + + return ( + + + + + + + + +

+ +
+ {firstName} + {email} +
+
+ + + + + + + Мой профиль + + + + + + + + Выйти + + + + + ); +} diff --git a/src/widgets/nav-user/ui/NavUserFallback.tsx b/src/widgets/nav-user/ui/NavUserFallback.tsx new file mode 100644 index 0000000..b90b127 --- /dev/null +++ b/src/widgets/nav-user/ui/NavUserFallback.tsx @@ -0,0 +1,5 @@ +import { Skeleton } from 'shared/ui'; + +export function NavUserFallback() { + return ; +} diff --git a/src/widgets/notifications/index.ts b/src/widgets/notifications/index.ts new file mode 100644 index 0000000..b8c2386 --- /dev/null +++ b/src/widgets/notifications/index.ts @@ -0,0 +1 @@ +export { Notifications } from './ui/Notifications'; diff --git a/src/widgets/notifications/ui/Notifications.tsx b/src/widgets/notifications/ui/Notifications.tsx new file mode 100644 index 0000000..626fe88 --- /dev/null +++ b/src/widgets/notifications/ui/Notifications.tsx @@ -0,0 +1,13 @@ +'use client'; + +import dynamic from 'next/dynamic'; +import { NotificationsFallback } from './NotificationsFallback'; + +const NotificationsContent = dynamic( + () => import('./NotificationsContent').then((mod) => mod.NotificationsContent), + { ssr: false, loading: () => } +); + +export function Notifications() { + return ; +} diff --git a/src/widgets/notifications/ui/NotificationsContent.tsx b/src/widgets/notifications/ui/NotificationsContent.tsx new file mode 100644 index 0000000..f1f11fb --- /dev/null +++ b/src/widgets/notifications/ui/NotificationsContent.tsx @@ -0,0 +1,68 @@ +'use client'; + +import { useQuery } from '@tanstack/react-query'; +import { Bell } from 'lucide-react'; +import { useState } from 'react'; +import { + Button, + DropdownMenu, + DropdownMenuContent, + DropdownMenuTrigger, + Empty, + EmptyDescription, + EmptyHeader, + EmptyMedia, + EmptyTitle, + Spinner, +} from 'shared/ui'; + +const notificationsQueryKey = ['notifications']; + +async function getNotifications() { + // TODO: Replace mocked request with real notifications endpoint. + await new Promise((resolve) => setTimeout(resolve, 400)); + + return []; +} + +export function NotificationsContent() { + const [open, setOpen] = useState(false); + const query = useQuery({ + queryKey: notificationsQueryKey, + queryFn: getNotifications, + enabled: open, + }); + + return ( + + + + + + {query.isLoading ? ( +
+ + Загрузка уведомлений... +
+ ) : ( + + + + + + Уведомлений нет + Новые уведомления появятся здесь. + + + )} +
+
+ ); +} diff --git a/src/widgets/notifications/ui/NotificationsFallback.tsx b/src/widgets/notifications/ui/NotificationsFallback.tsx new file mode 100644 index 0000000..33f6032 --- /dev/null +++ b/src/widgets/notifications/ui/NotificationsFallback.tsx @@ -0,0 +1,5 @@ +import { Skeleton } from 'shared/ui'; + +export function NotificationsFallback() { + return ; +} diff --git a/src/widgets/page-layout/ui/PageLayout.tsx b/src/widgets/page-layout/ui/PageLayout.tsx index cd86624..f7d5533 100644 --- a/src/widgets/page-layout/ui/PageLayout.tsx +++ b/src/widgets/page-layout/ui/PageLayout.tsx @@ -18,7 +18,7 @@ export function PageLayout({ children, }: PageLayoutProps) { return ( -
+
diff --git a/src/widgets/quick-create/index.ts b/src/widgets/quick-create/index.ts new file mode 100644 index 0000000..cc0696e --- /dev/null +++ b/src/widgets/quick-create/index.ts @@ -0,0 +1 @@ +export { QuickCreate } from './ui/QuickCreate'; diff --git a/src/widgets/quick-create/ui/QuickCreate.tsx b/src/widgets/quick-create/ui/QuickCreate.tsx new file mode 100644 index 0000000..e7b2f9e --- /dev/null +++ b/src/widgets/quick-create/ui/QuickCreate.tsx @@ -0,0 +1,91 @@ +'use client'; + +import { useTeamStore } from 'entities/team'; +import { CreateProjectDialog } from 'features/projects/create'; +import { CreateTeamDialog } from 'features/teams/create'; +import { FolderPlus, Plus, UsersRound } from 'lucide-react'; +import { useState } from 'react'; +import { + Button, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, + Item, + ItemContent, + ItemDescription, + ItemMedia, + ItemTitle, +} from 'shared/ui'; + +export function QuickCreate() { + const slug = useTeamStore.use.slug(); + const [open, setOpen] = useState(false); + const [createTeamOpen, setCreateTeamOpen] = useState(false); + const [createProjectOpen, setCreateProjectOpen] = useState(false); + + return ( +
+ + + + + + { + e.preventDefault(); + setOpen(false); + setCreateTeamOpen(true); + }} + > + + + + + + Команда + + Создать новую команду + + + + + { + e.preventDefault(); + setOpen(false); + setCreateProjectOpen(true); + }} + > + + + + + + Проект + + Создать новый проект + + + + + + + + +
+ ); +}