Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements in CreateUserForm #9887

Open
wants to merge 33 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
63b9679
changes to create user form
rajku-dev Jan 10, 2025
ca7e588
Merge branch 'develop' into issue/9733/user-create-edit
rajku-dev Jan 11, 2025
768fe3d
Merge branch 'develop' into issue/9733/user-create-edit
rajku-dev Jan 11, 2025
c8d3420
send Date string to backend
rajku-dev Jan 12, 2025
cf3a717
switch back to input date
rajku-dev Jan 12, 2025
55c0457
fix error no show for password mismatch
rajku-dev Jan 12, 2025
8723c3b
Merge branch 'develop' into issue/9733/user-create-edit
rajku-dev Jan 12, 2025
f580cd8
Merge branch 'develop' into issue/9733/user-create-edit
rajku-dev Jan 13, 2025
819724b
Merge branch 'develop' into issue/9733/user-create-edit
rajku-dev Jan 13, 2025
957de3f
added extra layer for backend error
rajku-dev Jan 13, 2025
44584de
reuse translation
rajku-dev Jan 14, 2025
b0f3ee6
apply coderabbit suggestion
rajku-dev Jan 14, 2025
4c1f7d3
Revert "apply coderabbit suggestion"
rajku-dev Jan 14, 2025
d47073a
remove console logs
rajku-dev Jan 14, 2025
11b27db
fix translation
rajku-dev Jan 14, 2025
45370af
Merge branch 'develop' into issue/9733/user-create-edit
rajku-dev Jan 14, 2025
9d5f192
apply coderabbit suggestion
rajku-dev Jan 14, 2025
6083aa2
Merge branch 'issue/9733/user-create-edit' of https://github.com/rajk…
rajku-dev Jan 14, 2025
6359acf
back to refine
rajku-dev Jan 14, 2025
6d0901f
apply coderabbit suggestion
rajku-dev Jan 14, 2025
bb3919b
Merge branch 'develop' into issue/9733/user-create-edit
rajku-dev Jan 14, 2025
8ac0ef9
Merge branch 'develop' into issue/9733/user-create-edit
rajku-dev Jan 15, 2025
48550cf
Update src/components/Users/CreateUserForm.tsx
rajku-dev Jan 15, 2025
e5c2521
Merge branch 'develop' into issue/9733/user-create-edit
rajku-dev Jan 15, 2025
f40f508
fix lint
rajku-dev Jan 15, 2025
068798f
Add cypress id | Fix Lint
rajku-dev Jan 15, 2025
ab23bb7
Merge branch 'develop' into issue/9733/user-create-edit
rajku-dev Jan 16, 2025
b3f2495
fix cypress test
rajku-dev Jan 16, 2025
3450f20
Merge branch 'issue/9733/user-create-edit' of https://github.com/rajk…
rajku-dev Jan 16, 2025
c3f010d
remove isValid check
rajku-dev Jan 16, 2025
150f8aa
Merge branch 'develop' into issue/9733/user-create-edit
nihal467 Jan 16, 2025
2ebbcec
Merge branch 'develop' into issue/9733/user-create-edit
nihal467 Jan 16, 2025
c6d5598
Merge branch 'develop' into issue/9733/user-create-edit
rajku-dev Jan 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,7 @@
"date_of_admission": "Date of Admission",
"date_of_birth": "Date of Birth",
"date_of_birth_age": "Date of Birth/Age",
"date_of_birth_cannot_be_in_future": "Date of birth cannot be in the future",
"date_of_birth_or_age": "Date of Birth or Age",
"date_of_positive_covid_19_swab": "Date of Positive Covid 19 Swab",
"date_of_result": "Covid confirmation date",
Expand Down Expand Up @@ -1494,6 +1495,7 @@
"phone_number": "Phone Number",
"phone_number_at_current_facility": "Phone Number of Contact person at current Facility",
"phone_number_min_error": "Phone number must be at least 10 characters long",
"phone_number_must_start": "Phone number must start with +91 followed by 10 digits",
"phone_number_not_found": "Phone number not found",
"phone_number_validation": "Phone number must start with +91 followed by 10 digits",
"phone_number_verified": "Phone Number Verified",
Expand Down Expand Up @@ -1875,6 +1877,7 @@
"thank_you_for_choosing": "Thank you for choosing our care service",
"the_request_for_resources_placed_by_yourself_is": "The request for resource (details below) placed by yourself is",
"third_party_software_licenses": "Third Party Software Licenses",
"this_field_is_required": "This field is required",
"time": "Time",
"time_slot": "Time Slot",
"title_of_request": "Title of Request",
Expand Down
134 changes: 89 additions & 45 deletions src/components/Users/CreateUserForm.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { useQuery } from "@tanstack/react-query";
import { useMutation } from "@tanstack/react-query";
import { Loader2 } from "lucide-react";
rajku-dev marked this conversation as resolved.
Show resolved Hide resolved
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
Expand Down Expand Up @@ -32,8 +34,8 @@ import { validateRule } from "@/components/Users/UserFormValidations";

import { GENDER_TYPES } from "@/common/constants";

import mutate from "@/Utils/request/mutate";
import query from "@/Utils/request/query";
import request from "@/Utils/request/request";
import OrganizationSelector from "@/pages/Organization/components/OrganizationSelector";
import { UserBase } from "@/types/user/user";
import UserApi from "@/types/user/userApi";
Expand Down Expand Up @@ -66,23 +68,42 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
.regex(/[A-Z]/, t("password_uppercase_validation"))
.regex(/[0-9]/, t("password_number_validation")),
c_password: z.string(),
first_name: z.string().min(1, t("field_required")),
last_name: z.string().min(1, t("field_required")),
email: z.string().email(t("invalid_email_address")),
first_name: z.string().min(1, t("this_field_is_required")),
last_name: z.string().min(1, t("this_field_is_required")),
rajku-dev marked this conversation as resolved.
Show resolved Hide resolved
email: z.string().email(t("invalid_email")),
phone_number: z
.string()
.regex(/^\+91[0-9]{10}$/, t("phone_number_validation")),
.regex(/^\+91[0-9]{10}$/, t("phone_number_must_start")),
alt_phone_number: z
.string()
.regex(/^\+91[0-9]{10}$/, t("phone_number_validation"))
.refine(
(val) => val === "" || /^\+91[0-9]{10}$/.test(val),
t("phone_number_must_start"),
)
.transform((val) => val || undefined)
.optional(),
phone_number_is_whatsapp: z.boolean().default(true),
date_of_birth: z.string().min(1, t("field_required")),
gender: z.enum(["male", "female", "other"]),
qualification: z.string().optional(),
doctor_experience_commenced_on: z.string().optional(),
doctor_medical_council_registration: z.string().optional(),
geo_organization: z.string().min(1, t("field_required")),
date_of_birth: z
.string({
required_error: t("this_field_is_required"),
rajku-dev marked this conversation as resolved.
Show resolved Hide resolved
})
.refine((dob) => dob <= new Date().toISOString(), {
rajku-dev marked this conversation as resolved.
Show resolved Hide resolved
message: t("date_of_birth_cannot_be_in_future"),
}),
gender: z.enum(["male", "female", "transgender", "non_binary"]),
rajku-dev marked this conversation as resolved.
Show resolved Hide resolved
qualification: z
.string()
.optional()
.transform((val) => val || undefined),
doctor_experience_commenced_on: z
.string()
.optional()
.transform((val) => val || undefined),
doctor_medical_council_registration: z
.string()
.optional()
.transform((val) => val || undefined),
geo_organization: z.string().min(1, t("this_field_is_required")),
})
.refine((data) => data.password === data.c_password, {
message: t("password_mismatch"),
Expand All @@ -92,6 +113,7 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
type UserFormValues = z.infer<typeof userFormSchema>;

const form = useForm<UserFormValues>({
mode: "onChange",
resolver: zodResolver(userFormSchema),
defaultValues: {
user_type: "staff",
Expand Down Expand Up @@ -160,29 +182,39 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
}
};

const onSubmit = async (data: UserFormValues) => {
try {
const {
res,
data: user,
error,
} = await request(UserApi.create, {
body: {
...data,
// Omit c_password as it's not needed in the API
c_password: undefined,
} as unknown as UserBase,
const { mutate: createUser, isPending } = useMutation({
mutationFn: mutate(UserApi.create),
onSuccess: (user: UserBase) => {
toast.success(t("user_added_successfully"));
onSubmitSuccess?.(user!);
},
onError: (error) => {
const errors = (error.cause?.errors as any[]) || [];
errors.forEach((err) => {
const field = err.loc[0];
form.setError(field, { message: err.ctx.error });
});
},
});

if (res?.ok) {
toast.success(t("user_added_successfully"));
onSubmitSuccess?.(user!);
} else {
toast.error((error?.message as string) ?? t("user_add_error"));
}
} catch (error) {
toast.error(t("user_add_error"));
}
const onSubmit = (data: z.infer<typeof userFormSchema>) => {
createUser({
username: data.username,
first_name: data.first_name,
last_name: data.last_name,
email: data.email,
phone_number: data.phone_number,
alt_phone_number: data.alt_phone_number,
date_of_birth: data.date_of_birth,
geo_organization: data.geo_organization,
user_type: data.user_type,
gender: data.gender,
password: data.password,
qualification: data.qualification,
doctor_experience_commenced_on: data.doctor_experience_commenced_on,
doctor_medical_council_registration:
data.doctor_medical_council_registration,
});
rajku-dev marked this conversation as resolved.
Show resolved Hide resolved
};

return (
Expand All @@ -193,7 +225,7 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="user_type"
render={({ field }) => (
<FormItem>
<FormLabel>{t("user_type")}</FormLabel>
<FormLabel required>{t("user_type")}</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
Expand All @@ -218,7 +250,7 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="first_name"
render={({ field }) => (
<FormItem>
<FormLabel>{t("first_name")}</FormLabel>
<FormLabel required>{t("first_name")}</FormLabel>
<FormControl>
<Input placeholder={t("first_name")} {...field} />
</FormControl>
Expand All @@ -232,7 +264,7 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="last_name"
render={({ field }) => (
<FormItem>
<FormLabel>{t("last_name")}</FormLabel>
<FormLabel required>{t("last_name")}</FormLabel>
<FormControl>
<Input placeholder={t("last_name")} {...field} />
</FormControl>
Expand All @@ -246,7 +278,7 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>{t("username")}</FormLabel>
<FormLabel required>{t("username")}</FormLabel>
<FormControl>
<div className="relative">
<Input placeholder={t("username")} {...field} />
Expand All @@ -263,7 +295,7 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>{t("password")}</FormLabel>
<FormLabel required>{t("password")}</FormLabel>
<FormControl>
<PasswordInput placeholder={t("password")} {...field} />
</FormControl>
Expand All @@ -277,7 +309,7 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="c_password"
render={({ field }) => (
<FormItem>
<FormLabel>{t("confirm_password")}</FormLabel>
<FormLabel required>{t("confirm_password")}</FormLabel>
<FormControl>
<PasswordInput
placeholder={t("confirm_password")}
Expand All @@ -295,7 +327,7 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>{t("email")}</FormLabel>
<FormLabel required>{t("email")}</FormLabel>
<FormControl>
<Input type="email" placeholder={t("email")} {...field} />
</FormControl>
Expand All @@ -310,9 +342,14 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="phone_number"
render={({ field }) => (
<FormItem>
<FormLabel>{t("phone_number")}</FormLabel>
<FormLabel required>{t("phone_number")}</FormLabel>
<FormControl>
<Input type="tel" placeholder="+91XXXXXXXXXX" {...field} />
<Input
type="tel"
placeholder="+91XXXXXXXXXX"
maxLength={13}
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
Expand Down Expand Up @@ -365,7 +402,7 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="date_of_birth"
render={({ field }) => (
<FormItem>
<FormLabel>{t("date_of_birth")}</FormLabel>
<FormLabel required>{t("date_of_birth")}</FormLabel>
<FormControl>
<Input type="date" {...field} />
</FormControl>
Expand Down Expand Up @@ -476,8 +513,15 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
)}
/>

<Button type="submit" className="w-full">
{t("create_user")}
<Button
type="submit"
className="w-full"
disabled={
!form.formState.isDirty || !form.formState.isValid || isPending
rajku-dev marked this conversation as resolved.
Show resolved Hide resolved
}
>
{isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}{" "}
{t("create")}
</Button>
</form>
</Form>
Expand Down
4 changes: 3 additions & 1 deletion src/components/ui/date-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ import {
interface DatePickerProps {
date?: Date;
onChange?: (date?: Date) => void;
disabled?: (date: Date) => boolean;
}

export function DatePicker({ date, onChange }: DatePickerProps) {
export function DatePicker({ date, onChange, disabled }: DatePickerProps) {
const [open, setOpen] = useState(false);

return (
Expand All @@ -44,6 +45,7 @@ export function DatePicker({ date, onChange }: DatePickerProps) {
setOpen(false);
}}
initialFocus
disabled={disabled}
/>
</PopoverContent>
</Popover>
Expand Down
17 changes: 17 additions & 0 deletions src/types/user/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,20 @@ export type UserBase = {
phone_number: string;
gender: (typeof GENDER_TYPES)[number]["id"];
};

export type UserCreateRequest = {
user_type: UserType;
gender: (typeof GENDER_TYPES)[number]["id"];
password: string;
geo_organization: string;
username: string;
first_name: string;
last_name: string;
email: string;
phone_number: string;
alt_phone_number?: string;
date_of_birth: string;
qualification?: string;
doctor_experience_commenced_on?: string;
doctor_medical_council_registration?: string;
rajku-dev marked this conversation as resolved.
Show resolved Hide resolved
};
4 changes: 2 additions & 2 deletions src/types/user/userApi.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { HttpMethod, Type } from "@/Utils/request/api";
import { PaginatedResponse } from "@/Utils/request/types";
import { UserBase } from "@/types/user/user";
import { UserBase, UserCreateRequest } from "@/types/user/user";

export default {
list: {
Expand All @@ -12,7 +12,7 @@ export default {
path: "/api/v1/users/",
method: HttpMethod.POST,
TRes: Type<UserBase>(),
TBody: Type<UserBase>(),
TBody: Type<UserCreateRequest>(),
},
get: {
path: "/api/v1/users/{username}/",
Expand Down
Loading