refactor(v2): add common sidebar
This commit is contained in:
parent
68ace91321
commit
f9a51e4c79
@ -46,7 +46,7 @@ export const CommonList: React.FC<CommonListProps> = React.memo((props) => {
|
||||
const finalList = searchResult ?? props.items;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col h-full">
|
||||
{props.hasSearch && (
|
||||
<div className="bg-background/95 p-4 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||
<form>
|
||||
@ -63,7 +63,7 @@ export const CommonList: React.FC<CommonListProps> = React.memo((props) => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ScrollArea className="h-screen">
|
||||
<ScrollArea className="flex-1">
|
||||
<div className="flex flex-col gap-2 p-4 pt-0">
|
||||
{finalList.map((item) => {
|
||||
const isSelected = item.href === location.pathname;
|
||||
@ -105,7 +105,7 @@ export const CommonList: React.FC<CommonListProps> = React.memo((props) => {
|
||||
})}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
CommonList.displayName = 'CommonList';
|
||||
|
22
src/client/components/CommonSidebar.tsx
Normal file
22
src/client/components/CommonSidebar.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import { Separator } from './ui/separator';
|
||||
|
||||
interface CommonSidebarProps extends React.PropsWithChildren {
|
||||
header: React.ReactNode;
|
||||
}
|
||||
export const CommonSidebar: React.FC<CommonSidebarProps> = React.memo(
|
||||
(props) => {
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<div className="flex items-center px-4 py-2 h-[52px]">
|
||||
{props.header}
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="flex-1 overflow-hidden">{props.children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
CommonSidebar.displayName = 'CommonSidebar';
|
@ -24,7 +24,7 @@ import {
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { useEvent, useEventWithLoading } from '@/hooks/useEvent';
|
||||
import { useEvent } from '@/hooks/useEvent';
|
||||
import { Input } from '../ui/input';
|
||||
import { preventDefault } from '@/utils/dom';
|
||||
import { trpc } from '@/api/trpc';
|
||||
|
@ -17,14 +17,10 @@ import { cn } from '@/utils/style';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { Nav } from './Layout/Nav';
|
||||
import { WorkspaceSwitcher } from '@/components/WorkspaceSwitcher';
|
||||
import { ColorSchemeSwitcher } from '@/components/ColorSchemeSwitcher';
|
||||
import { LanguageSelector } from '@/components/LanguageSelector';
|
||||
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
|
||||
import { useUserInfo } from '@/store/user';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { UserConfig } from './Layout/UserConfig';
|
||||
import { Outlet } from '@tanstack/react-router';
|
||||
import { CommonList, CommonListItem } from '@/components/CommonList';
|
||||
import { trpc } from '@/api/trpc';
|
||||
import { useCurrentWorkspaceId } from '@/store/user';
|
||||
|
||||
const defaultLayout: [number, number, number] = [265, 440, 655];
|
||||
|
||||
@ -41,6 +37,10 @@ export const LayoutV2: React.FC<{
|
||||
defaultValue: false,
|
||||
}
|
||||
);
|
||||
const workspaceId = useCurrentWorkspaceId();
|
||||
const { data: serviceCount } = trpc.workspace.getServiceCount.useQuery({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return (
|
||||
<TooltipProvider delayDuration={0}>
|
||||
@ -83,13 +83,13 @@ export const LayoutV2: React.FC<{
|
||||
links={[
|
||||
{
|
||||
title: 'Website',
|
||||
label: '128',
|
||||
label: String(serviceCount?.website ?? ''),
|
||||
icon: LuAreaChart,
|
||||
variant: 'default',
|
||||
},
|
||||
{
|
||||
title: 'Monitor',
|
||||
label: '9',
|
||||
label: String(serviceCount?.monitor ?? ''),
|
||||
icon: LuMonitorDot,
|
||||
variant: 'ghost',
|
||||
},
|
||||
@ -101,13 +101,13 @@ export const LayoutV2: React.FC<{
|
||||
},
|
||||
{
|
||||
title: 'Telemetry',
|
||||
label: '',
|
||||
label: String(serviceCount?.telemetry ?? ''),
|
||||
icon: LuWifi,
|
||||
variant: 'ghost',
|
||||
},
|
||||
{
|
||||
title: 'Pages',
|
||||
label: '',
|
||||
label: String(serviceCount?.page ?? ''),
|
||||
icon: LuFilePieChart,
|
||||
variant: 'ghost',
|
||||
},
|
||||
@ -121,7 +121,7 @@ export const LayoutV2: React.FC<{
|
||||
</ResizablePanel>
|
||||
<ResizableHandle withHandle />
|
||||
<ResizablePanel defaultSize={layout[1]} minSize={30}>
|
||||
<div>{props.list}</div>
|
||||
<div className="h-full overflow-hidden">{props.list}</div>
|
||||
</ResizablePanel>
|
||||
<ResizableHandle withHandle />
|
||||
<ResizablePanel defaultSize={layout[2]}>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { trpc } from '@/api/trpc';
|
||||
import { CommonList } from '@/components/CommonList';
|
||||
import { CommonSidebar } from '@/components/CommonSidebar';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { AddWebsiteBtn } from '@/components/website/AddWebsiteBtn';
|
||||
import { useDataReady } from '@/hooks/useDataReady';
|
||||
@ -54,18 +55,19 @@ function WebsiteComponent() {
|
||||
return (
|
||||
<LayoutV2
|
||||
list={
|
||||
<div>
|
||||
<div className="flex items-center px-4 py-2">
|
||||
<CommonSidebar
|
||||
header={
|
||||
<>
|
||||
<h1 className="text-xl font-bold">{t('Website')}</h1>
|
||||
|
||||
<div className="ml-auto">
|
||||
<AddWebsiteBtn />
|
||||
</div>
|
||||
</div>
|
||||
<Separator />
|
||||
|
||||
</>
|
||||
}
|
||||
>
|
||||
<CommonList hasSearch={true} items={items} />
|
||||
</div>
|
||||
</CommonSidebar>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
@ -64,7 +64,7 @@ export const websiteRouter = router({
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'GET',
|
||||
path: `/workspace/{workspaceId}/website/all`,
|
||||
path: '/workspace/{workspaceId}/website/all',
|
||||
tags: [OPENAPI_TAG.WEBSITE],
|
||||
protect: true,
|
||||
},
|
||||
|
@ -1,8 +1,16 @@
|
||||
import { publicProcedure, router, workspaceOwnerProcedure } from '../trpc';
|
||||
import {
|
||||
OpenApiMetaInfo,
|
||||
publicProcedure,
|
||||
router,
|
||||
workspaceOwnerProcedure,
|
||||
workspaceProcedure,
|
||||
} from '../trpc';
|
||||
import { z } from 'zod';
|
||||
import { prisma } from '../../model/_client';
|
||||
import { workspaceDashboardLayoutSchema } from '../../model/_schema';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { OPENAPI_TAG } from '../../utils/const';
|
||||
import { OpenApiMeta } from 'trpc-openapi';
|
||||
|
||||
export const workspaceRouter = router({
|
||||
getUserWorkspaceRole: publicProcedure
|
||||
@ -27,6 +35,54 @@ export const workspaceRouter = router({
|
||||
|
||||
return relation?.role ?? null;
|
||||
}),
|
||||
getServiceCount: workspaceProcedure
|
||||
.meta(
|
||||
buildWorkspaceOpenapi({
|
||||
method: 'GET',
|
||||
path: '/getServiceCount',
|
||||
})
|
||||
)
|
||||
.output(
|
||||
z.object({
|
||||
website: z.number(),
|
||||
monitor: z.number(),
|
||||
telemetry: z.number(),
|
||||
page: z.number(),
|
||||
})
|
||||
)
|
||||
.query(async ({ input }) => {
|
||||
const { workspaceId } = input;
|
||||
|
||||
const [website, monitor, telemetry, page] = await Promise.all([
|
||||
prisma.website.count({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
}),
|
||||
prisma.monitor.count({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
}),
|
||||
prisma.telemetry.count({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
}),
|
||||
prisma.monitorStatusPage.count({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
return {
|
||||
website,
|
||||
monitor,
|
||||
telemetry,
|
||||
page,
|
||||
};
|
||||
}),
|
||||
updateDashboardOrder: workspaceOwnerProcedure
|
||||
.input(
|
||||
z.object({
|
||||
@ -64,3 +120,14 @@ export const workspaceRouter = router({
|
||||
});
|
||||
}),
|
||||
});
|
||||
|
||||
function buildWorkspaceOpenapi(meta: OpenApiMetaInfo): OpenApiMeta {
|
||||
return {
|
||||
openapi: {
|
||||
tags: [OPENAPI_TAG.WORKSPACE],
|
||||
protect: true,
|
||||
...meta,
|
||||
path: `/workspace/{workspaceId}${meta.path}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user