feat: allow rename workspace

This commit is contained in:
moonrailgun 2024-09-27 02:13:52 +08:00
parent 7c271dc3c1
commit 63e6bfe0d1
3 changed files with 97 additions and 6 deletions

View File

@ -3,7 +3,11 @@ 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, useHasAdminPermission } from '../../store/user';
import {
useCurrentWorkspace,
useHasAdminPermission,
useUserStore,
} from '../../store/user';
import { CommonHeader } from '@/components/CommonHeader';
import {
Card,
@ -40,6 +44,7 @@ import { z } from 'zod';
import { AlertConfirm } from '@/components/AlertConfirm';
import { ROLES } from '@tianji/shared';
import { cn } from '@/utils/style';
import { Separator } from '@/components/ui/separator';
export const Route = createFileRoute('/settings/workspace')({
beforeLoad: routeAuthBeforeLoad,
@ -63,6 +68,9 @@ function PageComponent() {
trpc.workspace.members.useQuery({
workspaceId,
});
const updateCurrentWorkspaceName = useUserStore(
(state) => state.updateCurrentWorkspaceName
);
const form = useForm<InviteFormValues>({
resolver: zodResolver(inviteFormSchema),
defaultValues: {
@ -73,12 +81,26 @@ function PageComponent() {
onSuccess: defaultSuccessHandler,
onError: defaultErrorHandler,
});
const renameWorkspaceMutation = trpc.workspace.rename.useMutation({
onSuccess: defaultSuccessHandler,
onError: defaultErrorHandler,
});
const deleteWorkspaceMutation = trpc.workspace.delete.useMutation({
onSuccess: defaultSuccessHandler,
onError: defaultErrorHandler,
});
const [handleInvite, isLoading] = useEventWithLoading(
const [renameWorkspaceName, setRenameWorkspaceName] = useState('');
const [handleRename, isRenameLoading] = useEventWithLoading(async () => {
await renameWorkspaceMutation.mutateAsync({
workspaceId,
name: renameWorkspaceName,
});
updateCurrentWorkspaceName(renameWorkspaceName);
});
const [handleInvite, isInviteLoading] = useEventWithLoading(
async (values: InviteFormValues) => {
await inviteMutation.mutateAsync({
workspaceId,
@ -177,7 +199,7 @@ function PageComponent() {
<CardFooter>
<Button
type="submit"
loading={isLoading}
loading={isInviteLoading}
disabled={!hasAdminPermission}
>
{t('Invite')}
@ -203,6 +225,33 @@ function PageComponent() {
</CardHeader>
<CardContent>
<div>
<div className="flex items-center gap-2 text-left">
<Input
className="w-60"
placeholder={t('New Workspace Name')}
value={renameWorkspaceName}
onChange={(e) => setRenameWorkspaceName(e.target.value)}
/>
<AlertConfirm
title={'Confirm to rename this workspace?'}
description={`${name} => ${renameWorkspaceName}`}
onConfirm={handleRename}
>
<Button
type="button"
loading={isRenameLoading}
disabled={
!renameWorkspaceName || name === renameWorkspaceName
}
>
{t('Rename')}
</Button>
</AlertConfirm>
</div>
<Separator className="my-4" />
<AlertConfirm
title={'Confirm to delete this workspace'}
description={t(

View File

@ -3,17 +3,31 @@ import { createWithEqualityFn } from 'zustand/traditional';
import { createSocketIOClient } from '../api/socketio';
import { AppRouterOutput } from '../api/trpc';
import { ROLES } from '@tianji/shared';
import { immer } from 'zustand/middleware/immer';
export type UserLoginInfo = NonNullable<AppRouterOutput['user']['info']>;
interface UserState {
info: UserLoginInfo | null;
updateCurrentWorkspaceName: (name: string) => void;
}
export const useUserStore = createWithEqualityFn<UserState>(
() => ({
export const useUserStore = createWithEqualityFn<UserState>()(
immer((set) => ({
info: null,
}),
updateCurrentWorkspaceName: (name) => {
const currentUserInfo = useUserStore.getState().info;
if (!currentUserInfo) {
return;
}
set((state) => {
for (const workspace of state.info?.workspaces ?? []) {
workspace.workspace.name = name;
}
});
},
})),
shallow
);

View File

@ -12,6 +12,7 @@ import { prisma } from '../../model/_client.js';
import {
userInfoSchema,
workspaceDashboardLayoutSchema,
workspaceSchema,
} from '../../model/_schema/index.js';
import { Prisma } from '@prisma/client';
import { OPENAPI_TAG } from '../../utils/const.js';
@ -124,6 +125,33 @@ export const workspaceRouter = router({
return userInfo;
}),
rename: workspaceOwnerProcedure
.meta(
buildWorkspaceOpenapi({
method: 'PATCH',
path: '/rename',
})
)
.input(
z.object({
name: z.string().max(60).min(4),
})
)
.output(workspaceSchema)
.mutation(async ({ input }) => {
const { workspaceId, name } = input;
const workspace = await prisma.workspace.update({
where: {
id: workspaceId,
},
data: {
name,
},
});
return workspace;
}),
delete: workspaceOwnerProcedure
.meta(
buildWorkspaceOpenapi({