Initial Release
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
|
||||
import React from "react";
|
||||
import { Form } from "@/components/ui/form";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { UseFormReturn } from "react-hook-form";
|
||||
import UserProfilePictureField from "./UserProfilePictureField";
|
||||
import UserTextField from "./UserTextField";
|
||||
import UserToggleField from "./UserToggleField";
|
||||
import { DialogFooter } from "@/components/ui/dialog";
|
||||
|
||||
interface AddUserFormProps {
|
||||
form: UseFormReturn<any>;
|
||||
onSubmit: (data: any) => void;
|
||||
isSubmitting: boolean;
|
||||
}
|
||||
|
||||
const AddUserForm = ({ form, onSubmit, isSubmitting }: AddUserFormProps) => {
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||
<UserProfilePictureField control={form.control} />
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<UserTextField
|
||||
control={form.control}
|
||||
name="full_name"
|
||||
label="Full Name"
|
||||
placeholder="Enter full name"
|
||||
/>
|
||||
|
||||
<UserTextField
|
||||
control={form.control}
|
||||
name="email"
|
||||
label="Email"
|
||||
placeholder="Enter email"
|
||||
type="email"
|
||||
/>
|
||||
|
||||
<UserTextField
|
||||
control={form.control}
|
||||
name="username"
|
||||
label="Username"
|
||||
placeholder="Enter username"
|
||||
/>
|
||||
|
||||
<UserTextField
|
||||
control={form.control}
|
||||
name="role"
|
||||
label="Role"
|
||||
placeholder="Enter role (e.g. admin, user)"
|
||||
/>
|
||||
|
||||
<UserTextField
|
||||
control={form.control}
|
||||
name="password"
|
||||
label="Password"
|
||||
placeholder="Enter password"
|
||||
type="password"
|
||||
/>
|
||||
|
||||
<UserTextField
|
||||
control={form.control}
|
||||
name="passwordConfirm"
|
||||
label="Confirm Password"
|
||||
placeholder="Confirm password"
|
||||
type="password"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<UserToggleField
|
||||
control={form.control}
|
||||
name="isActive"
|
||||
label="Active Status"
|
||||
description="User will be able to access the system"
|
||||
/>
|
||||
|
||||
<DialogFooter className="pt-4">
|
||||
<Button type="submit" disabled={isSubmitting}>
|
||||
{isSubmitting ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
Creating...
|
||||
</>
|
||||
) : (
|
||||
"Create User"
|
||||
)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddUserForm;
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
|
||||
import React from "react";
|
||||
import { FormField, FormItem, FormLabel, FormControl, FormMessage } from "@/components/ui/form";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { Control } from "react-hook-form";
|
||||
|
||||
// Custom interface for local profile images
|
||||
interface ProfileImage {
|
||||
url: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
// Define local profile image paths - include all uploaded SVG images
|
||||
const localProfileImages: ProfileImage[] = [
|
||||
{ url: "/upload/profile/profile1.svg", label: "Profile 1" },
|
||||
{ url: "/upload/profile/profile2.svg", label: "Profile 2" },
|
||||
{ url: "/upload/profile/profile3.svg", label: "Profile 3" },
|
||||
{ url: "/upload/profile/profile4.svg", label: "Profile 4" },
|
||||
{ url: "/upload/profile/profile5.svg", label: "Profile 5" },
|
||||
{ url: "/upload/profile/profile6.svg", label: "Profile 6" },
|
||||
{ url: "/upload/profile/profile7.svg", label: "Profile 7" },
|
||||
{ url: "/upload/profile/profile8.svg", label: "Profile 8" }
|
||||
];
|
||||
|
||||
interface UserProfilePictureFieldProps {
|
||||
control: Control<any>;
|
||||
}
|
||||
|
||||
const UserProfilePictureField = ({ control }: UserProfilePictureFieldProps) => {
|
||||
return (
|
||||
<FormField
|
||||
control={control}
|
||||
name="avatar"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Profile Picture</FormLabel>
|
||||
<RadioGroup
|
||||
onValueChange={field.onChange}
|
||||
value={field.value}
|
||||
className="grid grid-cols-3 gap-4"
|
||||
>
|
||||
{localProfileImages.map((avatar) => (
|
||||
<FormItem key={avatar.url} className="flex flex-col items-center justify-center space-y-1">
|
||||
<FormControl>
|
||||
<RadioGroupItem
|
||||
value={avatar.url}
|
||||
id={`new-${avatar.url}`}
|
||||
className="sr-only"
|
||||
/>
|
||||
</FormControl>
|
||||
<label
|
||||
htmlFor={`new-${avatar.url}`}
|
||||
className={`cursor-pointer rounded-md p-1 ${
|
||||
field.value === avatar.url
|
||||
? "ring-2 ring-primary ring-offset-2"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<Avatar className="h-16 w-16">
|
||||
<AvatarImage src={avatar.url} alt={avatar.label} />
|
||||
<AvatarFallback>?</AvatarFallback>
|
||||
</Avatar>
|
||||
</label>
|
||||
<div className="text-xs text-center">{avatar.label}</div>
|
||||
</FormItem>
|
||||
))}
|
||||
</RadioGroup>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserProfilePictureField;
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
import React from "react";
|
||||
import { FormField, FormItem, FormLabel, FormControl, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Control } from "react-hook-form";
|
||||
|
||||
interface UserTextFieldProps {
|
||||
control: Control<any>;
|
||||
name: string;
|
||||
label: string;
|
||||
placeholder: string;
|
||||
type?: string;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
const UserTextField = ({
|
||||
control,
|
||||
name,
|
||||
label,
|
||||
placeholder,
|
||||
type = "text",
|
||||
required = false
|
||||
}: UserTextFieldProps) => {
|
||||
return (
|
||||
<FormField
|
||||
control={control}
|
||||
name={name}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{label}
|
||||
{required && <span className="text-destructive ml-1">*</span>}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type={type}
|
||||
placeholder={placeholder}
|
||||
{...field}
|
||||
value={field.value || ''}
|
||||
onChange={e => {
|
||||
// Handle the value change properly
|
||||
const value = e.target.value;
|
||||
field.onChange(value);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserTextField;
|
||||
@@ -0,0 +1,39 @@
|
||||
|
||||
import React from "react";
|
||||
import { FormField, FormItem, FormLabel, FormControl, FormMessage, FormDescription } from "@/components/ui/form";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Control } from "react-hook-form";
|
||||
|
||||
interface UserToggleFieldProps {
|
||||
control: Control<any>;
|
||||
name: string;
|
||||
label: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const UserToggleField = ({ control, name, label, description }: UserToggleFieldProps) => {
|
||||
return (
|
||||
<FormField
|
||||
control={control}
|
||||
name={name}
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3">
|
||||
<div className="space-y-0.5">
|
||||
<FormLabel>{label}</FormLabel>
|
||||
<FormDescription>
|
||||
{description}
|
||||
</FormDescription>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserToggleField;
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
export { default as UserProfilePictureField } from './UserProfilePictureField';
|
||||
export { default as UserTextField } from './UserTextField';
|
||||
export { default as UserToggleField } from './UserToggleField';
|
||||
export { default as AddUserForm } from './AddUserForm';
|
||||
Reference in New Issue
Block a user