add function for user

- create house
- edit house
- delete house
- list all member for active house
This commit is contained in:
2026-02-08 13:43:14 +07:00
parent 42435faa7f
commit 1d3e79c546
40 changed files with 1006 additions and 170 deletions

View File

@@ -1,4 +1,3 @@
import { ReturnError } from '@/types/common';
import { useAppForm } from '@hooks/use-app-form';
import { m } from '@paraglide/messages';
import { Locale, setLocale } from '@paraglide/runtime';
@@ -22,7 +21,7 @@ const UserSettingsForm = () => {
const { data, isLoading } = useQuery(settingQueries.listUser());
const updateMutation = useMutation({
const { mutate: updateMutation, isPending } = useMutation({
mutationFn: updateUserSettings,
onSuccess: (_, variables) => {
setLocale(variables.data.language as Locale);
@@ -51,7 +50,7 @@ const UserSettingsForm = () => {
onChange: userSettingSchema,
},
onSubmit: ({ value }) => {
updateMutation.mutate({ data: value as UserSettingInput });
updateMutation({ data: value as UserSettingInput });
},
});
@@ -102,7 +101,10 @@ const UserSettingsForm = () => {
</form.AppField>
<Field>
<form.AppForm>
<form.SubscribeButton label={m.ui_update_btn()} />
<form.SubscribeButton
label={m.ui_update_btn()}
disabled={isPending}
/>
</form.AppForm>
</Field>
</FieldGroup>

View File

@@ -8,21 +8,31 @@ import * as ShadcnSelect from '@ui/select';
import { SelectUser as SelectUserUI } from '@ui/select-user';
import { Textarea } from '@ui/textarea';
import { type VariantProps } from 'class-variance-authority';
import { Spinner } from '../ui/spinner';
export function SubscribeButton({
label,
variant = 'default',
disabled = false,
}: {
label: string;
disabled?: boolean;
} & VariantProps<typeof buttonVariants>) {
const form = useFormContext();
return (
<form.Subscribe selector={(state) => state.isSubmitting}>
{(isSubmitting) => (
<Button type="submit" disabled={isSubmitting} variant={variant}>
{label}
</Button>
)}
{(isSubmitting) => {
return (
<Button
type="submit"
disabled={isSubmitting || disabled}
variant={variant}
>
{(isSubmitting || disabled) && <Spinner data-icon="inline-start" />}
{label}
</Button>
);
}}
</form.Subscribe>
);
}

View File

@@ -1,4 +1,4 @@
import { ReturnError } from '@/types/common';
import { useAuth } from '@/components/auth/auth-provider';
import { useAppForm } from '@hooks/use-app-form';
import useDebounced from '@hooks/use-debounced';
import { authClient } from '@lib/auth-client';
@@ -12,14 +12,16 @@ import { Button } from '@ui/button';
import { DialogClose, DialogFooter } from '@ui/dialog';
import { Field, FieldGroup } from '@ui/field';
import { slugify } from '@utils/helper';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { toast } from 'sonner';
type FormProps = {
onSubmit: (open: boolean) => void;
isPersonal?: boolean;
};
const CreateNewHouseForm = ({ onSubmit }: FormProps) => {
const CreateNewHouseForm = ({ onSubmit, isPersonal = false }: FormProps) => {
const { data: session } = useAuth();
const [userKeyword, setUserKeyword] = useState('');
const debouncedUserKeyword = useDebounced(userKeyword, 300);
const { data: users } = useQuery(
@@ -28,11 +30,13 @@ const CreateNewHouseForm = ({ onSubmit }: FormProps) => {
const queryClient = useQueryClient();
const { mutate: createHouseMutation } = useMutation({
const queryKey = isPersonal ? 'currentUser' : 'list';
const { mutate: createHouseMutation, isPending } = useMutation({
mutationFn: createHouse,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [...housesQueries.all, 'list'],
queryKey: [...housesQueries.all, queryKey],
});
onSubmit(false);
toast.success(m.houses_page_message_create_house_success(), {
@@ -76,6 +80,13 @@ const CreateNewHouseForm = ({ onSubmit }: FormProps) => {
},
});
useEffect(() => {
if (isPersonal) {
form.setFieldValue('userId', session.user.id);
}
console.log(isPending);
}, []);
return (
<form
id="admin-create-house-form"
@@ -94,17 +105,19 @@ const CreateNewHouseForm = ({ onSubmit }: FormProps) => {
<field.TextField type="color" label={m.houses_page_form_color()} />
)}
</form.AppField>
<form.AppField name="userId">
{(field) => (
<field.SelectUser
label={m.houses_page_form_create_for()}
values={users ?? []}
placeholder="Chọn người dùng"
keyword={userKeyword}
onKeywordChange={setUserKeyword}
/>
)}
</form.AppField>
{!isPersonal && (
<form.AppField name="userId">
{(field) => (
<field.SelectUser
label={m.houses_page_form_create_for()}
values={users ?? []}
placeholder="Chọn người dùng"
keyword={userKeyword}
onKeywordChange={setUserKeyword}
/>
)}
</form.AppField>
)}
<Field>
<DialogFooter>
<DialogClose asChild>
@@ -113,7 +126,10 @@ const CreateNewHouseForm = ({ onSubmit }: FormProps) => {
</Button>
</DialogClose>
<form.AppForm>
<form.SubscribeButton label={m.ui_confirm_btn()} />
<form.SubscribeButton
label={m.ui_confirm_btn()}
disabled={isPending}
/>
</form.AppForm>
</DialogFooter>
</Field>

View File

@@ -1,4 +1,3 @@
import { ReturnError } from '@/types/common';
import { useAppForm } from '@hooks/use-app-form';
import { authClient } from '@lib/auth-client';
import { m } from '@paraglide/messages';
@@ -16,18 +15,21 @@ import { toast } from 'sonner';
type EditHouseFormProps = {
data: OrganizationWithMembers;
onSubmit: (open: boolean) => void;
mutateKey: string;
};
const EditHouseForm = ({ data, onSubmit }: EditHouseFormProps) => {
const EditHouseForm = ({ data, onSubmit, mutateKey }: EditHouseFormProps) => {
const { refetch } = authClient.useActiveOrganization();
const queryClient = useQueryClient();
const { mutate: updateHouseMutation } = useMutation({
const { mutate: updateHouseMutation, isPending } = useMutation({
mutationFn: updateHouse,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [...housesQueries.all, 'list'],
queryKey: [...housesQueries.all, mutateKey],
});
onSubmit(false);
refetch();
toast.success(m.houses_page_message_update_house_success(), {
richColors: true,
});
@@ -104,7 +106,10 @@ const EditHouseForm = ({ data, onSubmit }: EditHouseFormProps) => {
</Button>
</DialogClose>
<form.AppForm>
<form.SubscribeButton label={m.ui_confirm_btn()} />
<form.SubscribeButton
label={m.ui_confirm_btn()}
disabled={isPending}
/>
</form.AppForm>
</DialogFooter>
</Field>

View File

@@ -1,4 +1,3 @@
import { ReturnError } from '@/types/common';
import { useAppForm } from '@hooks/use-app-form';
import { m } from '@paraglide/messages';
import { GearIcon } from '@phosphor-icons/react';
@@ -22,7 +21,7 @@ const SettingsForm = () => {
const { data: settings, isLoading } = useQuery(settingQueries.listAdmin());
const updateMutation = useMutation({
const { mutate: updateMutation, isPending } = useMutation({
mutationFn: updateAdminSettings,
onSuccess: () => {
queryClient.invalidateQueries(settingQueries.listAdmin());
@@ -51,7 +50,7 @@ const SettingsForm = () => {
onChange: settingSchema,
},
onSubmit: async ({ value }) => {
updateMutation.mutate({ data: value as SettingsInput });
updateMutation({ data: value as SettingsInput });
},
});
@@ -89,7 +88,10 @@ const SettingsForm = () => {
</form.AppField>
<Field>
<form.AppForm>
<form.SubscribeButton label={m.ui_update_btn()} />
<form.SubscribeButton
label={m.ui_update_btn()}
disabled={isPending}
/>
</form.AppForm>
</Field>
</FieldGroup>

View File

@@ -1,4 +1,3 @@
import { ReturnError } from '@/types/common';
import { useAppForm } from '@hooks/use-app-form';
import { m } from '@paraglide/messages';
import { usersQueries } from '@service/queries';
@@ -17,7 +16,7 @@ type FormProps = {
const AdminCreateUserForm = ({ onSubmit }: FormProps) => {
const queryClient = useQueryClient();
const { mutate: createUserMutation } = useMutation({
const { mutate: createUserMutation, isPending } = useMutation({
mutationFn: createUser,
onSuccess: () => {
queryClient.invalidateQueries({
@@ -100,7 +99,10 @@ const AdminCreateUserForm = ({ onSubmit }: FormProps) => {
</Button>
</DialogClose>
<form.AppForm>
<form.SubscribeButton label={m.ui_signup_btn()} />
<form.SubscribeButton
label={m.ui_signup_btn()}
disabled={isPending}
/>
</form.AppForm>
</DialogFooter>
</Field>

View File

@@ -1,5 +1,4 @@
import { Button } from '@/components/ui/button';
import { ReturnError } from '@/types/common';
import { useAppForm } from '@hooks/use-app-form';
import { m } from '@paraglide/messages';
import { usersQueries } from '@service/queries';
@@ -19,7 +18,7 @@ type FormProps = {
const AdminSetPasswordForm = ({ data, onSubmit }: FormProps) => {
const queryClient = useQueryClient();
const setUserPasswordMutation = useMutation({
const { mutate: setUserPasswordMutation, isPending } = useMutation({
mutationFn: setUserPassword,
onSuccess: () => {
queryClient.invalidateQueries({
@@ -50,7 +49,7 @@ const AdminSetPasswordForm = ({ data, onSubmit }: FormProps) => {
onSubmit: userSetPasswordSchema,
},
onSubmit: async ({ value }) => {
setUserPasswordMutation.mutate({ data: value });
setUserPasswordMutation({ data: value });
},
});
@@ -83,7 +82,10 @@ const AdminSetPasswordForm = ({ data, onSubmit }: FormProps) => {
</Button>
</DialogClose>
<form.AppForm>
<form.SubscribeButton label={m.ui_save_btn()} />
<form.SubscribeButton
label={m.ui_save_btn()}
disabled={isPending}
/>
</form.AppForm>
</DialogFooter>
</Field>

View File

@@ -1,4 +1,3 @@
import { ReturnError } from '@/types/common';
import { useAppForm } from '@hooks/use-app-form';
import { m } from '@paraglide/messages';
import { usersQueries } from '@service/queries';
@@ -24,7 +23,7 @@ const AdminSetUserRoleForm = ({ data, onSubmit }: SetRoleFormProps) => {
role: data.role,
};
const updateRoleMutation = useMutation({
const { mutate: updateRoleMutation, isPending } = useMutation({
mutationFn: setUserRole,
onSuccess: () => {
queryClient.refetchQueries({
@@ -53,7 +52,7 @@ const AdminSetUserRoleForm = ({ data, onSubmit }: SetRoleFormProps) => {
onSubmit: userUpdateRoleSchema,
},
onSubmit: async ({ value }) => {
updateRoleMutation.mutate({ data: value });
updateRoleMutation({ data: value });
},
});
@@ -90,7 +89,10 @@ const AdminSetUserRoleForm = ({ data, onSubmit }: SetRoleFormProps) => {
</Button>
</DialogClose>
<form.AppForm>
<form.SubscribeButton label={m.ui_save_btn()} />
<form.SubscribeButton
label={m.ui_save_btn()}
disabled={isPending}
/>
</form.AppForm>
</DialogFooter>
</Field>

View File

@@ -1,4 +1,3 @@
import { ReturnError } from '@/types/common';
import { useAppForm } from '@hooks/use-app-form';
import { m } from '@paraglide/messages';
import { usersQueries } from '@service/queries';
@@ -19,7 +18,7 @@ type UpdateUserFormProps = {
const AdminUpdateUserInfoForm = ({ data, onSubmit }: UpdateUserFormProps) => {
const queryClient = useQueryClient();
const updateUserMutation = useMutation({
const { mutate: updateUserMutation, isPending } = useMutation({
mutationFn: updateUserInformation,
onSuccess: () => {
queryClient.refetchQueries({
@@ -49,7 +48,7 @@ const AdminUpdateUserInfoForm = ({ data, onSubmit }: UpdateUserFormProps) => {
onChange: userUpdateInfoSchema,
},
onSubmit: async ({ value }) => {
updateUserMutation.mutate({ data: value });
updateUserMutation({ data: value });
},
});
@@ -77,7 +76,10 @@ const AdminUpdateUserInfoForm = ({ data, onSubmit }: UpdateUserFormProps) => {
</Button>
</DialogClose>
<form.AppForm>
<form.SubscribeButton label={m.ui_save_btn()} />
<form.SubscribeButton
label={m.ui_save_btn()}
disabled={isPending}
/>
</form.AppForm>
</DialogFooter>
</Field>