invite member to house
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { useFieldContext, useFormContext } from '@hooks/use-app-form';
|
||||
import { RoleEnum } from '@service/user.schema';
|
||||
import { useStore } from '@tanstack/react-form';
|
||||
import { Button, buttonVariants } from '@ui/button';
|
||||
import { Field, FieldError, FieldLabel } from '@ui/field';
|
||||
@@ -146,12 +145,11 @@ export function Select({
|
||||
label,
|
||||
values,
|
||||
placeholder,
|
||||
isRole = false,
|
||||
// isRole = false,
|
||||
}: {
|
||||
label: string;
|
||||
values: Array<{ label: string; value: string }>;
|
||||
placeholder?: string;
|
||||
isRole?: boolean;
|
||||
}) {
|
||||
const field = useFieldContext<string>();
|
||||
const errors = useStore(field.store, (state) => state.meta.errors);
|
||||
@@ -163,11 +161,7 @@ export function Select({
|
||||
<ShadcnSelect.Select
|
||||
name={field.name}
|
||||
value={String(field.state.value)}
|
||||
onValueChange={(value) =>
|
||||
isRole
|
||||
? field.handleChange(RoleEnum.parse(value))
|
||||
: field.handleChange(value)
|
||||
}
|
||||
onValueChange={(value) => field.handleChange(value)}
|
||||
>
|
||||
<ShadcnSelect.SelectTrigger aria-invalid={isInvalid}>
|
||||
<ShadcnSelect.SelectValue placeholder={placeholder} />
|
||||
@@ -235,6 +229,7 @@ export function SelectUser({
|
||||
keyword,
|
||||
onKeywordChange,
|
||||
searchPlaceholder = 'Tìm theo tên hoặc email...',
|
||||
selectKey = 'id',
|
||||
}: {
|
||||
label: string;
|
||||
values: Array<{ id: string; name: string; email: string }>;
|
||||
@@ -243,6 +238,7 @@ export function SelectUser({
|
||||
keyword?: string;
|
||||
onKeywordChange?: (value: string) => void;
|
||||
searchPlaceholder?: string;
|
||||
selectKey?: 'id' | 'email';
|
||||
}) {
|
||||
const field = useFieldContext<string>();
|
||||
const errors = useStore(field.store, (state) => state.meta.errors);
|
||||
@@ -262,6 +258,7 @@ export function SelectUser({
|
||||
onKeywordChange={onKeywordChange}
|
||||
searchPlaceholder={searchPlaceholder}
|
||||
aria-invalid={isInvalid}
|
||||
selectKey={selectKey}
|
||||
/>
|
||||
{isInvalid && <FieldError errors={errors} />}
|
||||
</Field>
|
||||
|
||||
@@ -84,7 +84,6 @@ const CreateNewHouseForm = ({ onSubmit, isPersonal = false }: FormProps) => {
|
||||
if (isPersonal) {
|
||||
form.setFieldValue('userId', session.user.id);
|
||||
}
|
||||
console.log(isPending);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -111,7 +110,8 @@ const CreateNewHouseForm = ({ onSubmit, isPersonal = false }: FormProps) => {
|
||||
<field.SelectUser
|
||||
label={m.houses_page_form_create_for()}
|
||||
values={users ?? []}
|
||||
placeholder="Chọn người dùng"
|
||||
placeholder={m.houses_page_form_user_select_placeholder()}
|
||||
searchPlaceholder={m.houses_page_form_user_select_search_placeholder()}
|
||||
keyword={userKeyword}
|
||||
onKeywordChange={setUserKeyword}
|
||||
/>
|
||||
|
||||
132
src/components/form/house/user-invite-member-form.tsx
Normal file
132
src/components/form/house/user-invite-member-form.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { DialogClose, DialogFooter } from '@/components/ui/dialog';
|
||||
import { useAppForm } from '@/hooks/use-app-form';
|
||||
import useDebounced from '@/hooks/use-debounced';
|
||||
import { authClient } from '@/lib/auth-client';
|
||||
import { m } from '@/paraglide/messages';
|
||||
import { invitationMember } from '@/service/house.api';
|
||||
import {
|
||||
invitationCreateBESchema,
|
||||
invitationCreateFESchema,
|
||||
} from '@/service/house.schema';
|
||||
import { housesQueries, usersQueries } from '@/service/queries';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { Field, FieldGroup, FieldLabel } from '@ui/field';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
type FormProps = {
|
||||
onSubmit: (open: boolean) => void;
|
||||
};
|
||||
|
||||
const UserInviteMemberForm = ({ onSubmit }: FormProps) => {
|
||||
const { data: activeHouse, refetch } = authClient.useActiveOrganization();
|
||||
const [userKeyword, setUserKeyword] = useState('');
|
||||
const debouncedUserKeyword = useDebounced(userKeyword, 300);
|
||||
const { data: users } = useQuery(
|
||||
usersQueries.select({ keyword: debouncedUserKeyword }, true),
|
||||
);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
if (!activeHouse) return null;
|
||||
|
||||
const { mutate: invitationMemberMutation, isPending } = useMutation({
|
||||
mutationFn: invitationMember,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [...housesQueries.all, 'currentUser'],
|
||||
});
|
||||
onSubmit(false);
|
||||
refetch();
|
||||
toast.success(m.houses_page_message_create_house_success(), {
|
||||
richColors: true,
|
||||
});
|
||||
},
|
||||
onError: (error: ReturnError) => {
|
||||
console.error(error);
|
||||
const code = error.code as Parameters<
|
||||
typeof m.backend_message
|
||||
>[0]['code'];
|
||||
toast.error(m.backend_message({ code }), {
|
||||
richColors: true,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const form = useAppForm({
|
||||
defaultValues: {
|
||||
email: '',
|
||||
houseId: activeHouse.id,
|
||||
role: '',
|
||||
},
|
||||
validators: {
|
||||
onChange: invitationCreateFESchema,
|
||||
onSubmit: invitationCreateFESchema,
|
||||
},
|
||||
onSubmit: async ({ value }) => {
|
||||
invitationMemberMutation({ data: invitationCreateBESchema.parse(value) });
|
||||
},
|
||||
});
|
||||
return (
|
||||
<form
|
||||
id="user-invite-member-form"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
form.handleSubmit();
|
||||
}}
|
||||
>
|
||||
<FieldGroup>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="house_name">
|
||||
{m.houses_page_form_name()}:
|
||||
</FieldLabel>
|
||||
<div className="flex gap-2">{activeHouse.name}</div>
|
||||
</Field>
|
||||
<form.AppField name="email">
|
||||
{(field) => (
|
||||
<field.SelectUser
|
||||
label={m.houses_page_form_user()}
|
||||
values={users ?? []}
|
||||
placeholder={m.houses_page_form_user_select_placeholder()}
|
||||
searchPlaceholder={m.houses_page_form_user_select_search_placeholder()}
|
||||
keyword={userKeyword}
|
||||
onKeywordChange={setUserKeyword}
|
||||
selectKey="email"
|
||||
/>
|
||||
)}
|
||||
</form.AppField>
|
||||
<form.AppField name="role">
|
||||
{(field) => (
|
||||
<field.Select
|
||||
label={m.profile_form_role()}
|
||||
placeholder={m.users_page_ui_select_placeholder_role()}
|
||||
values={[
|
||||
{ value: 'owner', label: m.role_tags({ role: 'owner' }) },
|
||||
{ value: 'admin', label: m.role_tags({ role: 'admin' }) },
|
||||
{ value: 'member', label: m.role_tags({ role: 'member' }) },
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</form.AppField>
|
||||
<Field>
|
||||
<DialogFooter>
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline" type="button">
|
||||
{m.ui_cancel_btn()}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<form.AppForm>
|
||||
<form.SubscribeButton
|
||||
label={m.ui_confirm_btn()}
|
||||
disabled={isPending}
|
||||
/>
|
||||
</form.AppForm>
|
||||
</DialogFooter>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserInviteMemberForm;
|
||||
@@ -2,7 +2,7 @@ import { useAppForm } from '@hooks/use-app-form';
|
||||
import { m } from '@paraglide/messages';
|
||||
import { usersQueries } from '@service/queries';
|
||||
import { createUser } from '@service/user.api';
|
||||
import { userCreateSchema } from '@service/user.schema';
|
||||
import { userCreateBESchema, userCreateFESchema } from '@service/user.schema';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { Button } from '@ui/button';
|
||||
import { DialogClose, DialogFooter } from '@ui/dialog';
|
||||
@@ -46,11 +46,11 @@ const AdminCreateUserForm = ({ onSubmit }: FormProps) => {
|
||||
role: '',
|
||||
},
|
||||
validators: {
|
||||
onChange: userCreateSchema,
|
||||
onSubmit: userCreateSchema,
|
||||
onChange: userCreateFESchema,
|
||||
onSubmit: userCreateFESchema,
|
||||
},
|
||||
onSubmit: ({ value }) => {
|
||||
createUserMutation({ data: userCreateSchema.parse(value) });
|
||||
createUserMutation({ data: userCreateBESchema.parse(value) });
|
||||
},
|
||||
});
|
||||
|
||||
@@ -83,7 +83,6 @@ const AdminCreateUserForm = ({ onSubmit }: FormProps) => {
|
||||
<field.Select
|
||||
label={m.profile_form_role()}
|
||||
placeholder={m.users_page_ui_select_placeholder_role()}
|
||||
isRole
|
||||
values={[
|
||||
{ value: 'admin', label: m.role_tags({ role: 'admin' }) },
|
||||
{ value: 'user', label: m.role_tags({ role: 'user' }) },
|
||||
|
||||
@@ -2,7 +2,10 @@ import { useAppForm } from '@hooks/use-app-form';
|
||||
import { m } from '@paraglide/messages';
|
||||
import { usersQueries } from '@service/queries';
|
||||
import { setUserRole } from '@service/user.api';
|
||||
import { userUpdateRoleSchema } from '@service/user.schema';
|
||||
import {
|
||||
userUpdateRoleBESchema,
|
||||
userUpdateRoleSchema,
|
||||
} from '@service/user.schema';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { Button } from '@ui/button';
|
||||
import { DialogClose, DialogFooter } from '@ui/dialog';
|
||||
@@ -52,7 +55,7 @@ const AdminSetUserRoleForm = ({ data, onSubmit }: SetRoleFormProps) => {
|
||||
onSubmit: userUpdateRoleSchema,
|
||||
},
|
||||
onSubmit: async ({ value }) => {
|
||||
updateRoleMutation({ data: value });
|
||||
updateRoleMutation({ data: userUpdateRoleBESchema.parse(value) });
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user