feat: add workspace page
This commit is contained in:
parent
b862dd7427
commit
491807165c
@ -28,6 +28,7 @@ import { Route as TelemetryAddImport } from './routes/telemetry/add'
|
||||
import { Route as TelemetryTelemetryIdImport } from './routes/telemetry/$telemetryId'
|
||||
import { Route as SurveyAddImport } from './routes/survey/add'
|
||||
import { Route as StatusSlugImport } from './routes/status/$slug'
|
||||
import { Route as SettingsWorkspaceImport } from './routes/settings/workspace'
|
||||
import { Route as SettingsUsageImport } from './routes/settings/usage'
|
||||
import { Route as SettingsProfileImport } from './routes/settings/profile'
|
||||
import { Route as SettingsNotificationsImport } from './routes/settings/notifications'
|
||||
@ -132,6 +133,11 @@ const StatusSlugRoute = StatusSlugImport.update({
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const SettingsWorkspaceRoute = SettingsWorkspaceImport.update({
|
||||
path: '/workspace',
|
||||
getParentRoute: () => SettingsRoute,
|
||||
} as any)
|
||||
|
||||
const SettingsUsageRoute = SettingsUsageImport.update({
|
||||
path: '/usage',
|
||||
getParentRoute: () => SettingsRoute,
|
||||
@ -292,6 +298,10 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof SettingsUsageImport
|
||||
parentRoute: typeof SettingsImport
|
||||
}
|
||||
'/settings/workspace': {
|
||||
preLoaderRoute: typeof SettingsWorkspaceImport
|
||||
parentRoute: typeof SettingsImport
|
||||
}
|
||||
'/status/$slug': {
|
||||
preLoaderRoute: typeof StatusSlugImport
|
||||
parentRoute: typeof rootRoute
|
||||
@ -374,6 +384,7 @@ export const routeTree = rootRoute.addChildren([
|
||||
SettingsNotificationsRoute,
|
||||
SettingsProfileRoute,
|
||||
SettingsUsageRoute,
|
||||
SettingsWorkspaceRoute,
|
||||
]),
|
||||
SurveyRoute.addChildren([
|
||||
SurveyAddRoute,
|
||||
|
@ -34,6 +34,11 @@ function PageComponent() {
|
||||
title: t('Notifications'),
|
||||
href: '/settings/notifications',
|
||||
},
|
||||
{
|
||||
id: 'workspace',
|
||||
title: t('Workspace'),
|
||||
href: '/settings/workspace',
|
||||
},
|
||||
{
|
||||
id: 'auditLog',
|
||||
title: t('Audit Log'),
|
||||
|
94
src/client/routes/settings/workspace.tsx
Normal file
94
src/client/routes/settings/workspace.tsx
Normal file
@ -0,0 +1,94 @@
|
||||
import { routeAuthBeforeLoad } from '@/utils/route';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { useTranslation } from '@i18next-toolkit/react';
|
||||
import { CommonWrapper } from '@/components/CommonWrapper';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { useCurrentWorkspace } from '../../store/user';
|
||||
import { CommonHeader } from '@/components/CommonHeader';
|
||||
import { Card, CardContent, CardHeader } from '@/components/ui/card';
|
||||
import { Typography } from 'antd';
|
||||
import { AppRouterOutput, trpc } from '@/api/trpc';
|
||||
import { createColumnHelper, DataTable } from '@/components/DataTable';
|
||||
import { useMemo } from 'react';
|
||||
import { get } from 'lodash-es';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
|
||||
export const Route = createFileRoute('/settings/workspace')({
|
||||
beforeLoad: routeAuthBeforeLoad,
|
||||
component: PageComponent,
|
||||
});
|
||||
|
||||
type MemberInfo = AppRouterOutput['workspace']['members'][number];
|
||||
const columnHelper = createColumnHelper<MemberInfo>();
|
||||
|
||||
function PageComponent() {
|
||||
const { t } = useTranslation();
|
||||
const { id, name } = useCurrentWorkspace();
|
||||
const { data: members = [] } = trpc.workspace.members.useQuery({
|
||||
workspaceId: id,
|
||||
});
|
||||
|
||||
const columns = useMemo(() => {
|
||||
return [
|
||||
columnHelper.accessor(
|
||||
(data) =>
|
||||
get(data, ['user', 'nickname']) || get(data, ['user', 'username']),
|
||||
{
|
||||
header: t('Name'),
|
||||
size: 300,
|
||||
}
|
||||
),
|
||||
columnHelper.accessor('user.email', {
|
||||
header: t('Email'),
|
||||
size: 130,
|
||||
cell: (props) => {
|
||||
return (
|
||||
<span>
|
||||
{props.getValue()}
|
||||
{props.row.original.user.emailVerified && (
|
||||
<Badge className="ml-1">{t('Verified')}</Badge>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor('role', {
|
||||
header: t('Role'),
|
||||
size: 130,
|
||||
}),
|
||||
];
|
||||
}, [t]);
|
||||
|
||||
return (
|
||||
<CommonWrapper header={<CommonHeader title={t('Workspace')} />}>
|
||||
<ScrollArea className="h-full overflow-hidden p-4">
|
||||
<div className="flex flex-col gap-4">
|
||||
<Card>
|
||||
<CardHeader className="text-lg font-bold">
|
||||
{t('Current Workspace:')} {name}
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div>
|
||||
<span className="mr-2">{t('Workspace ID')}:</span>
|
||||
<span>
|
||||
<Typography.Text code={true} copyable={true}>
|
||||
{id}
|
||||
</Typography.Text>
|
||||
</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader className="text-lg font-bold">
|
||||
{t('Members')}
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<DataTable columns={columns} data={members} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</CommonWrapper>
|
||||
);
|
||||
}
|
@ -18,6 +18,7 @@ import { OpenApiMeta } from 'trpc-openapi';
|
||||
import { getServerCount } from '../../model/serverStatus.js';
|
||||
import { ROLES, slugRegex } from '@tianji/shared';
|
||||
import { createUserSelect } from '../../model/user.js';
|
||||
import { WorkspacesOnUsersModelSchema } from '../../prisma/zod/workspacesonusers.js';
|
||||
|
||||
export const workspaceRouter = router({
|
||||
create: protectProedure
|
||||
@ -150,6 +151,48 @@ export const workspaceRouter = router({
|
||||
},
|
||||
});
|
||||
}),
|
||||
members: workspaceProcedure
|
||||
.meta(
|
||||
buildWorkspaceOpenapi({
|
||||
method: 'GET',
|
||||
path: '/{workspaceId}/members',
|
||||
})
|
||||
)
|
||||
.output(
|
||||
z.array(
|
||||
WorkspacesOnUsersModelSchema.merge(
|
||||
z.object({
|
||||
user: z.object({
|
||||
username: z.string(),
|
||||
nickname: z.string().nullable(),
|
||||
email: z.string().nullable(),
|
||||
emailVerified: z.date().nullable(),
|
||||
}),
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
.query(async ({ input }) => {
|
||||
const { workspaceId } = input;
|
||||
|
||||
const list = await prisma.workspacesOnUsers.findMany({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
username: true,
|
||||
nickname: true,
|
||||
email: true,
|
||||
emailVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return list;
|
||||
}),
|
||||
getUserWorkspaceRole: publicProcedure
|
||||
.input(
|
||||
z.object({
|
||||
|
Loading…
Reference in New Issue
Block a user