audio + fix bug
deploy to danak / build_and_deploy (push) Has been cancelled

This commit is contained in:
hamid zarghami
2026-06-03 14:39:16 +03:30
parent 3bfd5fd1eb
commit 9b72105115
8 changed files with 331 additions and 233 deletions
+35 -20
View File
@@ -1,36 +1,51 @@
import { type FC } from "react"; import { type FC } from "react";
import { DocumentUpload, Music } from "iconsax-react"; import { DocumentUpload, Music } from "iconsax-react";
import { clx } from "@/helpers/utils";
type AudioUploadZoneProps = { type AudioUploadZoneProps = {
getRootProps: () => React.HTMLAttributes<HTMLDivElement>; getRootProps: () => React.HTMLAttributes<HTMLDivElement>;
getInputProps: () => React.InputHTMLAttributes<HTMLInputElement>; getInputProps: () => React.InputHTMLAttributes<HTMLInputElement>;
isLoading?: boolean;
loadingLabel?: string;
}; };
const AudioUploadZone: FC<AudioUploadZoneProps> = ({ getRootProps, getInputProps }) => { const AudioUploadZone: FC<AudioUploadZoneProps> = ({
getRootProps,
getInputProps,
isLoading = false,
loadingLabel = "در حال آپلود...",
}) => {
return ( return (
<div <div
{...getRootProps()} {...getRootProps()}
className="w-full py-8 border border-dashed border-description flex flex-col justify-center items-center gap-4 rounded-2xl cursor-pointer hover:bg-gray-50 transition-colors" className={clx(
"w-full py-8 border border-dashed border-description flex flex-col justify-center items-center gap-4 rounded-2xl transition-colors",
isLoading
? "pointer-events-none opacity-80"
: "cursor-pointer hover:bg-gray-50"
)}
> >
<input {...getInputProps()} /> <input {...getInputProps()} />
<Music {isLoading ? (
size={32} <>
color="#8C90A3" <span className="h-8 w-8 animate-spin rounded-full border-2 border-gray-300 border-t-black" />
variant="Outline" <div className="text-description text-xs">{loadingLabel}</div>
/> </>
<div className="text-description text-xs"> ) : (
فایل صوتی مورد نظر را آپلود کنید <>
</div> <Music size={32} color="#8C90A3" variant="Outline" />
<div className="text-description text-[10px]"> <div className="text-description text-xs">
mp3, wav, ogg, m4a, aac, flac فایل صوتی مورد نظر را آپلود کنید
</div> </div>
<div className="flex items-center gap-2"> <div className="text-description text-[10px]">
<DocumentUpload mp3, wav, ogg, m4a, aac, flac
size={16} </div>
color="black" <div className="flex items-center gap-2">
/> <DocumentUpload size={16} color="black" />
<div className="text-xs">آپلود</div> <div className="text-xs">آپلود</div>
</div> </div>
</>
)}
</div> </div>
); );
}; };
+14 -5
View File
@@ -2,6 +2,7 @@ export const Paths = {
home: "/", home: "/",
editor: "/editor", editor: "/editor",
viewer: "/viewer", viewer: "/viewer",
viewerBySlug: "/catalogue",
catalog: { catalog: {
list: "/catalogue", list: "/catalogue",
}, },
@@ -11,12 +12,20 @@ export const Paths = {
}; };
const viewerPathPrefix = `${Paths.viewer}/`; const viewerPathPrefix = `${Paths.viewer}/`;
const viewerBySlugPathPrefix = `${Paths.viewerBySlug}/`;
/** Full-screen viewer: /viewer/:id or /catalogue/:slug (not the list at /catalogue). */
export const isViewerPath = (pathname: string): boolean => {
const isViewerById =
pathname.startsWith(viewerPathPrefix) &&
pathname.length > viewerPathPrefix.length;
const isViewerBySlug =
pathname.startsWith(viewerBySlugPathPrefix) &&
pathname.length > viewerBySlugPathPrefix.length;
return isViewerById || isViewerBySlug;
};
/** Routes that do not require login (must stay in sync with PrivateRoute requireAuth={false}) */ /** Routes that do not require login (must stay in sync with PrivateRoute requireAuth={false}) */
export const isPublicPath = (pathname: string): boolean => { export const isPublicPath = (pathname: string): boolean => {
return ( return pathname === Paths.home || isViewerPath(pathname);
pathname === Paths.home ||
(pathname.startsWith(viewerPathPrefix) &&
pathname.length > viewerPathPrefix.length)
);
}; };
@@ -68,7 +68,7 @@ const CatalogueItem: FC<Props> = ({ item, refetch }) => {
</div> </div>
<div className="flex justify-end gap-1 items-center mt-1"> <div className="flex justify-end gap-1 items-center mt-1">
<Link <Link
to={Paths.viewer + `/${item.slug}`} to={Paths.viewerBySlug + `/${item.slug}`}
onClick={requestViewerFullscreen} onClick={requestViewerFullscreen}
> >
<div className="size-6 bg-[#EAECF4] rounded-md flex justify-center items-center"> <div className="size-6 bg-[#EAECF4] rounded-md flex justify-center items-center">
@@ -1,4 +1,4 @@
import { type FC, useCallback, useMemo } from "react"; import { type FC, useCallback, useMemo, useState } from "react";
import { useEditorStore, type ToolType } from "@/pages/editor/store/editorStore"; import { useEditorStore, type ToolType } from "@/pages/editor/store/editorStore";
import { useSingleUpload } from "@/pages/uploader/hooks/useUploaderData"; import { useSingleUpload } from "@/pages/uploader/hooks/useUploaderData";
import { CloseCircle, Music } from "iconsax-react"; import { CloseCircle, Music } from "iconsax-react";
@@ -13,30 +13,38 @@ const AudioInput: FC = () => {
const { objects, addObject, setSelectedObjectId, setTool, deleteObject } = const { objects, addObject, setSelectedObjectId, setTool, deleteObject } =
useEditorStore(); useEditorStore();
const { mutateAsync: uploadFile } = useSingleUpload(); const { mutateAsync: uploadFile } = useSingleUpload();
const [isUploading, setIsUploading] = useState(false);
const onDrop = useCallback( const onDrop = useCallback(
async (acceptedFiles: File[]) => { async (acceptedFiles: File[]) => {
for (const file of acceptedFiles) { if (acceptedFiles.length === 0) return;
try {
const result = await uploadFile(file); setIsUploading(true);
const audioUrl = result?.data?.url; try {
if (!audioUrl) continue; for (const file of acceptedFiles) {
const audioId = `audio-${Date.now()}-${Math.random()}`; try {
const newAudio = { const result = await uploadFile(file);
id: audioId, const audioUrl = result?.data?.url;
type: "audio" as ToolType, if (!audioUrl) continue;
x: 100, const audioId = `audio-${Date.now()}-${Math.random()}`;
y: 100, const newAudio = {
width: 320, id: audioId,
height: 56, type: "audio" as ToolType,
audioUrl, x: 100,
}; y: 100,
addObject(newAudio); width: 320,
setSelectedObjectId(newAudio.id); height: 56,
setTool("select"); audioUrl,
} catch { };
// TODO: show error toast addObject(newAudio);
setSelectedObjectId(newAudio.id);
setTool("select");
} catch {
// TODO: show error toast
}
} }
} finally {
setIsUploading(false);
} }
}, },
[addObject, setSelectedObjectId, setTool, uploadFile] [addObject, setSelectedObjectId, setTool, uploadFile]
@@ -46,6 +54,7 @@ const AudioInput: FC = () => {
onDrop, onDrop,
accept: AUDIO_ACCEPT, accept: AUDIO_ACCEPT,
multiple: true, multiple: true,
disabled: isUploading,
}); });
const audioObjects = useMemo(() => { const audioObjects = useMemo(() => {
@@ -63,6 +72,7 @@ const AudioInput: FC = () => {
<AudioUploadZone <AudioUploadZone
getRootProps={getRootProps} getRootProps={getRootProps}
getInputProps={getInputProps} getInputProps={getInputProps}
isLoading={isUploading}
/> />
{audioObjects.length > 0 && ( {audioObjects.length > 0 && (
+14
View File
@@ -21,6 +21,11 @@ export const useGetCatalog = () => {
}); });
}; };
const catalogMongoIdPattern = /^[a-f\d]{24}$/i;
export const isCatalogMongoId = (value: string) =>
catalogMongoIdPattern.test(value);
export const useGetCatalogById = (id: string) => { export const useGetCatalogById = (id: string) => {
return useQuery({ return useQuery({
queryKey: ["catalog", id], queryKey: ["catalog", id],
@@ -31,6 +36,15 @@ export const useGetCatalogById = (id: string) => {
staleTime: 5 * 60 * 1000, staleTime: 5 * 60 * 1000,
}); });
}; };
/** Route param may be Mongo id (editor/admin) or slug (public viewer-style URLs). */
export const useGetCatalogByIdOrSlug = (param: string) => {
const isId = isCatalogMongoId(param);
const byId = useGetCatalogById(isId ? param : "");
const bySlug = useGetCatalogBySlug(isId ? "" : param);
return isId ? byId : bySlug;
};
export const useGetCatalogBySlug = (slug: string) => { export const useGetCatalogBySlug = (slug: string) => {
return useQuery({ return useQuery({
queryKey: ["catalog", slug], queryKey: ["catalog", slug],
+13 -3
View File
@@ -1,6 +1,9 @@
import { usePageTitle } from "@/hooks/usePageTitle"; import { usePageTitle } from "@/hooks/usePageTitle";
import type { DocumentSettings } from "@/pages/editor/store/editorStore"; import type { DocumentSettings } from "@/pages/editor/store/editorStore";
import { useGetCatalogBySlug } from "@/pages/home/hooks/useHomeData"; import {
useGetCatalogById,
useGetCatalogBySlug,
} from "@/pages/home/hooks/useHomeData";
import { type FC, useEffect, useState } from "react"; import { type FC, useEffect, useState } from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import BookViewer from "./components/BookViewer"; import BookViewer from "./components/BookViewer";
@@ -10,9 +13,16 @@ import { useInitialPagesPreload } from "./hooks/useInitialPagesPreload";
import type { PageData } from "./types"; import type { PageData } from "./types";
import { transformViewerDataToPages } from "./utils/dataTransformer"; import { transformViewerDataToPages } from "./utils/dataTransformer";
const Viewer: FC = () => { type ViewerProps = {
bySlug?: boolean;
};
const Viewer: FC<ViewerProps> = ({ bySlug = false }) => {
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
const { data, isLoading, error: queryError } = useGetCatalogBySlug(id ?? ""); const {
data,
isLoading,
error: queryError,
} = bySlug ? useGetCatalogBySlug(id ?? "") : useGetCatalogById(id ?? "");
usePageTitle(`کاتالوگ ${data?.data?.name}`); usePageTitle(`کاتالوگ ${data?.data?.name}`);
const [pages, setPages] = useState<PageData[]>([]); const [pages, setPages] = useState<PageData[]>([]);
const [documentSettings, setDocumentSettings] = useState< const [documentSettings, setDocumentSettings] = useState<
+112 -93
View File
@@ -1,98 +1,117 @@
import SideBar from '@/shared/SideBar' import { isViewerPath, Paths } from "@/config/Paths";
import Header from '@/shared/Header' import { clx } from "@/helpers/utils";
import { Route, Routes, useLocation } from 'react-router-dom' import CatalogueList from "@/pages/catalogue/List";
import { Paths } from '@/config/Paths' import DesignerRequest from "@/pages/designer/Request";
import PrivateRoute from '@/router/PrivateRoute' import Editor from "@/pages/editor/Editor";
import { clx } from '@/helpers/utils' import Home from "@/pages/home/Home";
import { useSharedStore } from '@/shared/store/sharedStore' import Viewer from "@/pages/viewer/Viewer";
import Home from '@/pages/home/Home' import PrivateRoute from "@/router/PrivateRoute";
import Editor from '@/pages/editor/Editor' import Header from "@/shared/Header";
import Viewer from '@/pages/viewer/Viewer' import SideBar from "@/shared/SideBar";
import CatalogueList from '@/pages/catalogue/List' import { useSharedStore } from "@/shared/store/sharedStore";
import DesignerRequest from '@/pages/designer/Request' import { Route, Routes, useLocation } from "react-router-dom";
const MainRouter = () => { const MainRouter = () => {
const { hasSubMenu } = useSharedStore() const { hasSubMenu } = useSharedStore();
const location = useLocation() const location = useLocation();
const viewerPathPrefix = `${Paths.viewer}/` const isViewerRoute = isViewerPath(location.pathname);
const isViewerRoute = const hiddenSideBarRoutes = [Paths.editor];
location.pathname.startsWith(viewerPathPrefix) && const shouldRenderSideBar =
location.pathname.length > viewerPathPrefix.length !hiddenSideBarRoutes.includes(location.pathname) && !isViewerRoute;
const hiddenSideBarRoutes = [Paths.editor] const routeHasLocalSidebar = location.pathname.includes("editor");
const shouldRenderSideBar = const hasSidebarSpace = shouldRenderSideBar || routeHasLocalSidebar;
!hiddenSideBarRoutes.includes(location.pathname) && !isViewerRoute const headerSidebarSize = routeHasLocalSidebar ? "wide" : "default";
const routeHasLocalSidebar = location.pathname.includes('editor')
const hasSidebarSpace = shouldRenderSideBar || routeHasLocalSidebar
const headerSidebarSize = routeHasLocalSidebar ? 'wide' : 'default'
return ( return (
<div className={clx( <div
'flex min-h-full flex-col overflow-hidden', className={clx(
isViewerRoute ? 'fixed inset-0 z-50 bg-neutral-100' : 'p-4', "flex min-h-full flex-col overflow-hidden",
)}> isViewerRoute ? "fixed inset-0 z-50 bg-neutral-100" : "p-4",
{!isViewerRoute && shouldRenderSideBar ? <SideBar /> : null} )}
{!isViewerRoute ? ( >
<Header hasMainSidebar={hasSidebarSpace} sidebarSize={headerSidebarSize} /> {!isViewerRoute && shouldRenderSideBar ? <SideBar /> : null}
) : null} {!isViewerRoute ? (
<div className={clx( <Header
'flex flex-1 flex-col min-h-0', hasMainSidebar={hasSidebarSpace}
!isViewerRoute && 'mt-[68px] xl:mt-[81px]', sidebarSize={headerSidebarSize}
!isViewerRoute && shouldRenderSideBar && 'xl:ms-[269px]', />
!isViewerRoute && shouldRenderSideBar && hasSubMenu && 'xl:ms-[305px]', ) : null}
!isViewerRoute && routeHasLocalSidebar && 'xl:mr-[374px]', <div
)}> className={clx(
<div className={clx( "flex flex-1 flex-col min-h-0",
'flex-1 flex flex-col w-full min-h-0', !isViewerRoute && "mt-[68px] xl:mt-[81px]",
isViewerRoute ? 'h-full overflow-hidden' : 'overflow-auto max-h-[calc(100vh-113px)]', !isViewerRoute && shouldRenderSideBar && "xl:ms-[269px]",
)}> !isViewerRoute &&
<div className='flex-1 h-full flex min-h-0'> shouldRenderSideBar &&
<Routes> hasSubMenu &&
<Route "xl:ms-[305px]",
path={Paths.home} !isViewerRoute && routeHasLocalSidebar && "xl:mr-[374px]",
element={ )}
<PrivateRoute requireAuth={false}> >
<Home /> <div
</PrivateRoute> className={clx(
} "flex-1 flex flex-col w-full min-h-0",
/> isViewerRoute
<Route ? "h-full overflow-hidden"
path={Paths.editor + '/:id'} : "overflow-auto max-h-[calc(100vh-113px)]",
element={ )}
<PrivateRoute> >
<Editor /> <div className="flex-1 h-full flex min-h-0">
</PrivateRoute> <Routes>
} <Route
/> path={Paths.home}
<Route element={
path={Paths.viewer + '/:id'} <PrivateRoute requireAuth={false}>
element={ <Home />
<PrivateRoute requireAuth={false}> </PrivateRoute>
<Viewer /> }
</PrivateRoute> />
} <Route
/> path={Paths.editor + "/:id"}
<Route element={
path={Paths.catalog.list} <PrivateRoute>
element={ <Editor />
<PrivateRoute> </PrivateRoute>
<CatalogueList /> }
</PrivateRoute> />
} <Route
/> path={Paths.viewer + "/:id"}
<Route element={
path={Paths.designer.request} <PrivateRoute requireAuth={false}>
element={ <Viewer />
<PrivateRoute> </PrivateRoute>
<DesignerRequest /> }
</PrivateRoute> />
} <Route
/> path={Paths.viewerBySlug + "/:id"}
</Routes> element={
</div> <PrivateRoute requireAuth={false}>
</div> <Viewer bySlug={true} />
</div> </PrivateRoute>
}
/>
<Route
path={Paths.catalog.list}
element={
<PrivateRoute>
<CatalogueList />
</PrivateRoute>
}
/>
<Route
path={Paths.designer.request}
element={
<PrivateRoute>
<DesignerRequest />
</PrivateRoute>
}
/>
</Routes>
</div>
</div> </div>
) </div>
} </div>
);
};
export default MainRouter export default MainRouter;
+111 -90
View File
@@ -1,102 +1,123 @@
import { type FC, useEffect } from 'react' import Input from "@/components/Input";
import Input from '@/components/Input' import { type FC, useEffect } from "react";
// import { ArrowDown2, CloseCircle, HambergerMenu, Logout, Wallet } from 'iconsax-react' // import { ArrowDown2, CloseCircle, HambergerMenu, Logout, Wallet } from 'iconsax-react'
// import AvatarImage from '../assets/images/avatar_image.png' // import AvatarImage from '../assets/images/avatar_image.png'
import { useTranslation } from 'react-i18next' import { Paths } from "@/config/Paths";
import { Link, useLocation } from 'react-router-dom' import { clx } from "@/helpers/utils";
import { Paths } from '@/config/Paths' import {
import { useSharedStore } from '@/shared/store/sharedStore' isCatalogMongoId,
import { Eye, HambergerMenu } from 'iconsax-react' useGetCatalogByIdOrSlug,
import { clx } from '@/helpers/utils' } from "@/pages/home/hooks/useHomeData";
import { requestViewerFullscreen } from '@/pages/viewer/utils/viewerFullscreen' import { useSharedStore } from "@/shared/store/sharedStore";
import { Eye, HambergerMenu } from "iconsax-react";
import { useTranslation } from "react-i18next";
import { Link, useLocation } from "react-router-dom";
type SidebarSize = 'default' | 'wide' type SidebarSize = "default" | "wide";
type HeaderProps = { type HeaderProps = {
hasMainSidebar?: boolean hasMainSidebar?: boolean;
sidebarSize?: SidebarSize sidebarSize?: SidebarSize;
} };
const sidebarSizeClasses: Record<SidebarSize, { base: string; withSub: string }> = { const sidebarSizeClasses: Record<
default: { SidebarSize,
base: 'xl:right-[285px] xl:w-[calc(100%-305px)]', { base: string; withSub: string }
withSub: 'xl:right-[320px] xl:w-[calc(100%-340px)]', > = {
}, default: {
wide: { base: "xl:right-[285px] xl:w-[calc(100%-305px)]",
base: 'xl:right-[390px] xl:w-[calc(100%-406px)]', withSub: "xl:right-[320px] xl:w-[calc(100%-340px)]",
withSub: 'xl:right-[425px] xl:w-[calc(100%-441px)]', },
}, wide: {
} base: "xl:right-[390px] xl:w-[calc(100%-406px)]",
withSub: "xl:right-[425px] xl:w-[calc(100%-441px)]",
},
};
const Header: FC<HeaderProps> = ({ hasMainSidebar = true, sidebarSize = 'default' }) => { const Header: FC<HeaderProps> = ({
hasMainSidebar = true,
sidebarSize = "default",
}) => {
// const location = useLocation();
// const [popoverKey, setPopoverKey] = useState(0);
const { t } = useTranslation("global");
const { setOpenSidebar, openSidebar, hasSubMenu, setSearch } =
useSharedStore();
const location = useLocation();
const editorPathPrefix = `${Paths.editor}/`;
const isEditorRoute =
location.pathname.startsWith(editorPathPrefix) &&
location.pathname.length > editorPathPrefix.length;
const editorRouteParam = isEditorRoute
? (location.pathname.slice(editorPathPrefix.length).split("/")[0] ?? "")
: "";
const { data: catalogData } = useGetCatalogByIdOrSlug(
isEditorRoute && editorRouteParam ? editorRouteParam : "",
);
const viewerSlug =
catalogData?.data?.slug ??
(editorRouteParam && !isCatalogMongoId(editorRouteParam)
? editorRouteParam
: "");
// const location = useLocation(); // useEffect(() => {
// const [popoverKey, setPopoverKey] = useState(0); // setPopoverKey((prevKey) => prevKey + 1);
const { t } = useTranslation('global') // }, [location.pathname]);
const { setOpenSidebar, openSidebar, hasSubMenu, setSearch } = useSharedStore()
const location = useLocation()
const editorPathPrefix = `${Paths.editor}/`
const isEditorRoute =
location.pathname.startsWith(editorPathPrefix) &&
location.pathname.length > editorPathPrefix.length
const editorDocumentId = isEditorRoute
? (location.pathname.slice(editorPathPrefix.length).split('/')[0] ?? '')
: ''
// useEffect(() => { // const handleLogout = () => {
// setPopoverKey((prevKey) => prevKey + 1); // removeToken()
// }, [location.pathname]); // removeRefreshToken()
// window.location.href = Pages.auth.login
// }
// const handleLogout = () => { useEffect(() => {
// removeToken() setSearch("");
// removeRefreshToken() // eslint-disable-next-line react-hooks/exhaustive-deps
// window.location.href = Pages.auth.login }, [location.pathname]);
// }
useEffect(() => { return (
setSearch('') <div
// eslint-disable-next-line react-hooks/exhaustive-deps className={clx(
}, [location.pathname]) "fixed z-10 right-4 left-4 top-4 xl:h-16 h-12 flex items-center px-6 bg-white justify-between rounded-[32px]",
hasMainSidebar &&
(hasSubMenu
? sidebarSizeClasses[sidebarSize].withSub
: sidebarSizeClasses[sidebarSize].base),
)}
>
<div className="min-w-[270px] hidden xl:block">
<Input
variant="search"
placeholder={t("header.search")}
onChangeSearchFinal={(value) => setSearch(value)}
/>
</div>
<div
onClick={() => setOpenSidebar(!openSidebar)}
className="xl:hidden block"
>
<HambergerMenu size={24} color="black" />
</div>
{/* <img src={LogoImage} className='h-6 xl:hidden block absolute right-0 left-0 mx-auto' /> */}
<div className="flex xl:gap-6 gap-4 items-center">
{isEditorRoute && viewerSlug ? (
<Link
target="_blank"
to={`${Paths.viewer}/${viewerSlug}`}
className="flex items-center"
aria-label={t("header.open_viewer")}
title={t("header.open_viewer")}
// onClick={requestViewerFullscreen}
>
<Eye size={20} color="black" />
</Link>
) : null}
return ( {/* <Link className='xl:hidden' to={Paths.wallet}>
<div className={clx(
'fixed z-10 right-4 left-4 top-4 xl:h-16 h-12 flex items-center px-6 bg-white justify-between rounded-[32px]',
hasMainSidebar && (
hasSubMenu
? sidebarSizeClasses[sidebarSize].withSub
: sidebarSizeClasses[sidebarSize].base
)
)}>
<div className='min-w-[270px] hidden xl:block'>
<Input
variant='search'
placeholder={t('header.search')}
onChangeSearchFinal={(value) => setSearch(value)}
/>
</div>
<div onClick={() => setOpenSidebar(!openSidebar)} className='xl:hidden block'>
<HambergerMenu size={24} color='black' />
</div>
{/* <img src={LogoImage} className='h-6 xl:hidden block absolute right-0 left-0 mx-auto' /> */}
<div className='flex xl:gap-6 gap-4 items-center'>
{isEditorRoute && editorDocumentId ? (
<Link
to={`${Paths.viewer}/${editorDocumentId}`}
className='flex items-center'
aria-label={t('header.open_viewer')}
title={t('header.open_viewer')}
onClick={requestViewerFullscreen}
>
<Eye size={20} color='black' />
</Link>
) : null}
{/* <Link className='xl:hidden' to={Paths.wallet}>
<Wallet className='xl:size-[18px] size-[17px]' color='#da2129' /> <Wallet className='xl:size-[18px] size-[17px]' color='#da2129' />
</Link> */} </Link> */}
{/* <Notifications /> */} {/* <Notifications /> */}
{/* { {/* {
data && ( data && (
<Popover className="relative" key={popoverKey}> <Popover className="relative" key={popoverKey}>
<PopoverButton > <PopoverButton >
@@ -144,9 +165,9 @@ const Header: FC<HeaderProps> = ({ hasMainSidebar = true, sidebarSize = 'default
</Popover> </Popover>
) )
} */} } */}
</div> </div>
</div> </div>
) );
} };
export default Header export default Header;