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

Username validation added in Create user form #9835

Merged
merged 18 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
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
10 changes: 9 additions & 1 deletion public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@
"allow_transfer": "Allow Transfer",
"allowed_formats_are": "Allowed formats are",
"already_a_member": "Already a member?",
"alternate_phone_number": "Alternate Phone Number",
"ambulance_driver_name": "Name of ambulance driver",
"ambulance_number": "Ambulance No",
"ambulance_phone_number": "Phone number of Ambulance",
Expand Down Expand Up @@ -503,6 +504,7 @@
"check_policy_eligibility": "Check Policy Eligibility",
"check_status": "Check Status",
"checked_in": "Checked-In",
"checking_availability": "Checking Availability",
"checking_consent_status": "Consent request status is being checked!",
"checking_eligibility": "Checking Eligibility",
"checking_for_update": "Checking for update",
Expand Down Expand Up @@ -647,6 +649,7 @@
"create_position_preset_description": "Creates a new position preset in Care from the current position of the camera for the given name",
"create_preset_prerequisite": "To create presets for this bed, you'll need to link the camera to the bed first.",
"create_resource_request": "Create Request",
"create_user": "Create User",
"created": "Created",
"created_by": "Created By",
"created_date": "Created Date",
Expand Down Expand Up @@ -728,6 +731,7 @@
"dob": "DOB",
"dob_format": "Please enter date in DD/MM/YYYY format",
"doc_will_visit_patient": "will visit the patient at the scheduled time.",
"doctor": "Doctor",
"doctor_experience_error": "Please enter a valid number between 0 and 100.",
"doctor_experience_required": "Years of experience is required",
"doctor_not_found": "Doctor not found",
Expand Down Expand Up @@ -772,7 +776,7 @@
"eg_mail_example_com": "Eg. mail@example.com",
"eg_xyz": "Eg. XYZ",
"eligible": "Eligible",
"email": "Email Address",
"email": "Email",
"email_address": "Email Address",
"email_discharge_summary_description": "Enter your valid email address to receive the discharge summary",
"email_success": "We will be sending an email shortly. Please check your inbox.",
Expand Down Expand Up @@ -1304,6 +1308,7 @@
"number_of_beds_out_of_range_error": "Number of beds cannot be greater than 100",
"number_of_chronic_diseased_dependents": "Number Of Chronic Diseased Dependents",
"number_of_covid_vaccine_doses": "Number of Covid vaccine doses",
"nurse": "Nurse",
"nursing_care": "Nursing Care",
"nursing_information": "Nursing Information",
"nutrition": "Nutrition",
Expand Down Expand Up @@ -1706,6 +1711,7 @@
"source": "Source",
"spokes": "Spoke Facilities",
"srf_id": "SRF ID",
"staff": "Staff",
"staff_list": "Staff List",
"start_consultation": "Start Consultation",
"start_datetime": "Start Date/Time",
Expand Down Expand Up @@ -1911,6 +1917,7 @@
"vitals_monitor": "Vitals Monitor",
"vitals_present": "Vitals Monitor present",
"voice_autofill": "Voice Autofill",
"volunteer": "Volunteer",
"volunteer_assigned": "Volunteer assigned successfully",
"volunteer_contact": "Volunteer Contact",
"volunteer_contact_detail": "Provide the name and contact details of a volunteer who can assist the patient in emergencies. This should be someone outside the family.",
Expand All @@ -1923,6 +1930,7 @@
"weekly_working_hours_error": "Average weekly working hours must be a number between 0 and 168",
"what_facility_assign_the_patient_to": "What facility would you like to assign the patient to",
"whatsapp_number": "Whatsapp Number",
"whatsapp_number_same_as_phone_number": "WhatsApp number is same as phone number",
"why_the_asset_is_not_working": "Why the asset is not working?",
"width": "Width ({{unit}})",
"with": "with",
Expand Down
117 changes: 84 additions & 33 deletions src/components/Users/CreateUserForm.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { useQuery } from "@tanstack/react-query";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import * as z from "zod";

import CareIcon from "@/CAREUI/icons/CareIcon";

import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {
Expand All @@ -23,13 +26,17 @@ import {
SelectValue,
} from "@/components/ui/select";

import { validateRule } from "@/components/Users/UserFormValidations";

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

import * as Notification from "@/Utils/Notifications";
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";
import userApi from "@/types/user/userApi";

const userFormSchema = z
.object({
Expand Down Expand Up @@ -107,14 +114,52 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
});

const userType = form.watch("user_type");
const usernameInput = form.watch("username");
const phoneNumber = form.watch("phone_number");
const isWhatsApp = form.watch("phone_number_is_whatsapp");

useEffect(() => {
if (isWhatsApp) {
form.setValue("alt_phone_number", phoneNumber);
}
}, [phoneNumber, isWhatsApp, form]);
if (usernameInput && usernameInput.length > 0) {
form.trigger("username");
}
}, [phoneNumber, isWhatsApp, form, usernameInput]);

const { error, isLoading } = useQuery({
queryKey: ["checkUsername", usernameInput],
queryFn: query(userApi.checkUsername, {
pathParams: { username: usernameInput },
silent: true,
}),
enabled: !form.formState.errors.username,
});

const renderUsernameFeedback = (usernameInput: string) => {
Rishith25 marked this conversation as resolved.
Show resolved Hide resolved
const {
errors: { username },
} = form.formState;
if (username?.message) {
return validateRule(false, username.message);
} else if (isLoading) {
return (
<div className="flex items-center gap-1">
<CareIcon
icon="l-spinner"
className="text-xl text-gray-500 animate-spin"
/>
<span className="text-gray-500 text-sm">
{t("checking_availability")}
</span>
</div>
);
} else if (error) {
return validateRule(false, t("username_not_available"));
} else if (usernameInput) {
return validateRule(true, t("username_available"));
}
};

const onSubmit = async (data: UserFormValues) => {
try {
Expand Down Expand Up @@ -155,18 +200,18 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="user_type"
render={({ field }) => (
<FormItem>
<FormLabel>User Type</FormLabel>
<FormLabel>{t("user_type")}</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select user type" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="doctor">Doctor</SelectItem>
<SelectItem value="nurse">Nurse</SelectItem>
<SelectItem value="staff">Staff</SelectItem>
<SelectItem value="volunteer">Volunteer</SelectItem>
<SelectItem value="doctor">{t("doctor")}</SelectItem>
<SelectItem value="nurse">{t("nurse")}</SelectItem>
<SelectItem value="staff">{t("staff")}</SelectItem>
<SelectItem value="volunteer">{t("volunteer")}</SelectItem>
</SelectContent>
</Select>
<FormMessage />
Expand All @@ -180,9 +225,9 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="first_name"
render={({ field }) => (
<FormItem>
<FormLabel>First Name</FormLabel>
<FormLabel>{t("first_name")}</FormLabel>
<FormControl>
<Input placeholder="First name" {...field} />
<Input placeholder={t("first_name")} {...field} />
</FormControl>
<FormMessage />
</FormItem>
Expand All @@ -194,26 +239,27 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="last_name"
render={({ field }) => (
<FormItem>
<FormLabel>Last Name</FormLabel>
<FormLabel>{t("last_name")}</FormLabel>
<FormControl>
<Input placeholder="Last name" {...field} />
<Input placeholder={t("last_name")} {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>

<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormLabel>{t("username")}</FormLabel>
<FormControl>
<Input placeholder="Username" {...field} />
<div className="relative">
<Input placeholder={t("username")} {...field} />
</div>
</FormControl>
<FormMessage />
{renderUsernameFeedback(usernameInput)}
</FormItem>
)}
/>
Expand All @@ -224,9 +270,13 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormLabel>{t("password")}</FormLabel>
<FormControl>
<Input type="password" placeholder="Password" {...field} />
<Input
type="password"
placeholder={t("password")}
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
Expand All @@ -238,11 +288,11 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="c_password"
render={({ field }) => (
<FormItem>
<FormLabel>Confirm Password</FormLabel>
<FormLabel>{t("confirm_password")}</FormLabel>
<FormControl>
<Input
type="password"
placeholder="Confirm password"
placeholder={t("confirm_password")}
{...field}
/>
</FormControl>
Expand All @@ -257,9 +307,9 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormLabel>{t("email")}</FormLabel>
<FormControl>
<Input type="email" placeholder="Email" {...field} />
<Input type="email" placeholder={t("email")} {...field} />
</FormControl>
<FormMessage />
</FormItem>
Expand All @@ -272,7 +322,7 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="phone_number"
render={({ field }) => (
<FormItem>
<FormLabel>Phone Number</FormLabel>
<FormLabel>{t("phone_number")}</FormLabel>
<FormControl>
<Input type="tel" placeholder="+91XXXXXXXXXX" {...field} />
</FormControl>
Expand All @@ -286,7 +336,7 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="alt_phone_number"
render={({ field }) => (
<FormItem>
<FormLabel>Alternative Phone Number</FormLabel>
<FormLabel>{t("alternate_phone_number")}</FormLabel>
<FormControl>
<Input
placeholder="+91XXXXXXXXXX"
Expand All @@ -313,7 +363,9 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
/>
</FormControl>
<div className="space-y-1 leading-none">
<FormLabel>WhatsApp number is same as phone number</FormLabel>
<FormLabel>
{t("whatsapp_number_same_as_phone_number")}
</FormLabel>
</div>
</FormItem>
)}
Expand All @@ -325,7 +377,7 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="date_of_birth"
render={({ field }) => (
<FormItem>
<FormLabel>Date of Birth</FormLabel>
<FormLabel>{t("date_of_birth")}</FormLabel>
<FormControl>
<Input type="date" {...field} />
</FormControl>
Expand All @@ -339,7 +391,7 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="gender"
render={({ field }) => (
<FormItem>
<FormLabel>Gender</FormLabel>
<FormLabel>{t("gender")}</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
Expand Down Expand Up @@ -369,9 +421,9 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="qualification"
render={({ field }) => (
<FormItem>
<FormLabel>Qualification</FormLabel>
<FormLabel>{t("qualification")}</FormLabel>
<FormControl>
<Input placeholder="Qualification" {...field} />
<Input placeholder={t("qualification")} {...field} />
</FormControl>
<FormMessage />
</FormItem>
Expand All @@ -387,11 +439,11 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="doctor_experience_commenced_on"
render={({ field }) => (
<FormItem>
<FormLabel>Years of Experience</FormLabel>
<FormLabel>{t("years_of_experience")}</FormLabel>
<FormControl>
<Input
type="number"
placeholder="Years of experience"
placeholder={t("years_of_experience")}
{...field}
/>
</FormControl>
Expand All @@ -405,10 +457,10 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
name="doctor_medical_council_registration"
render={({ field }) => (
<FormItem>
<FormLabel>Medical Council Registration</FormLabel>
<FormLabel>{t("medical_council_registration")}</FormLabel>
<FormControl>
<Input
placeholder="Medical council registration"
placeholder={t("medical_council_registration")}
{...field}
/>
</FormControl>
Expand All @@ -419,7 +471,6 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
</div>
</>
)}

<FormField
control={form.control}
name="geo_organization"
Expand All @@ -438,7 +489,7 @@ export default function CreateUserForm({ onSubmitSuccess }: Props) {
/>

<Button type="submit" className="w-full">
Create User
{t("create_user")}
</Button>
</form>
</Form>
Expand Down
5 changes: 5 additions & 0 deletions src/types/user/userApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@ export default {

TRes: Type<UserBase>(),
},
checkUsername: {
path: "/api/v1/users/{username}/check_availability/",
method: HttpMethod.GET,
TRes: Type<void>,
},
};
Loading