Initial commit for new panel
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
"use client";
|
||||
import InputGroup from "@/components/FormElements/InputGroup";
|
||||
import { ShowcaseSection } from "@/components/Layouts/showcase-section";
|
||||
import useCreateAdminPresenter from "./useCreateAdminPresenter";
|
||||
import { FormErrorMessages } from "@/constants/Texts";
|
||||
import { ICreateAdminCredentials } from "@/lib/api";
|
||||
import { FC, useEffect } from "react";
|
||||
interface IProps {
|
||||
editMode?: boolean;
|
||||
defaultValues?: ICreateAdminCredentials;
|
||||
id?: string;
|
||||
}
|
||||
const CreateAdminForm: FC<IProps> = ({ defaultValues, editMode, id }) => {
|
||||
const { errors, handleSubmit, register, router, onSubmit } =
|
||||
useCreateAdminPresenter({
|
||||
editMode,
|
||||
defaultValues,
|
||||
id,
|
||||
});
|
||||
|
||||
return (
|
||||
<form
|
||||
className="grid grid-cols-1 gap-9"
|
||||
onSubmit={handleSubmit((data) => {
|
||||
onSubmit(data);
|
||||
})}
|
||||
>
|
||||
<div className="flex flex-col gap-9">
|
||||
<ShowcaseSection
|
||||
title="Input Fields"
|
||||
className="grid grid-cols-2 gap-5"
|
||||
>
|
||||
<div>
|
||||
<InputGroup
|
||||
{...register("full_name", {
|
||||
required: { message: FormErrorMessages.required, value: true },
|
||||
minLength: {
|
||||
value: 5,
|
||||
message: FormErrorMessages.minLength(5),
|
||||
},
|
||||
maxLength: {
|
||||
value: 100,
|
||||
message: FormErrorMessages.maxLength(100),
|
||||
},
|
||||
})}
|
||||
label="Full name"
|
||||
placeholder="Full name"
|
||||
type="text"
|
||||
name="full_name"
|
||||
/>
|
||||
{errors.full_name && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.full_name.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<InputGroup
|
||||
label="Username"
|
||||
placeholder="Username"
|
||||
type="text"
|
||||
{...register("username", {
|
||||
required: {
|
||||
value: true,
|
||||
message: FormErrorMessages.required,
|
||||
},
|
||||
pattern: {
|
||||
value: /^[a-zA-Z0-9]+$/,
|
||||
message: FormErrorMessages.alphaNumeric,
|
||||
},
|
||||
minLength: {
|
||||
value: 3,
|
||||
message: FormErrorMessages.minLength(3),
|
||||
},
|
||||
maxLength: {
|
||||
value: 30,
|
||||
message: FormErrorMessages.maxLength(30),
|
||||
},
|
||||
})}
|
||||
name="username"
|
||||
/>
|
||||
{errors.username && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.username.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<InputGroup
|
||||
label="Email"
|
||||
placeholder="Email"
|
||||
type="email"
|
||||
{...register("email", {
|
||||
required: {
|
||||
value: true,
|
||||
message: FormErrorMessages.required,
|
||||
},
|
||||
pattern: {
|
||||
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
||||
message: FormErrorMessages.invalidEmail,
|
||||
},
|
||||
})}
|
||||
name="email"
|
||||
/>
|
||||
{errors.email && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.email.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<InputGroup
|
||||
label="Password"
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
{...register("password", {
|
||||
required: {
|
||||
value: true,
|
||||
message: FormErrorMessages.required,
|
||||
},
|
||||
minLength: {
|
||||
value: 8,
|
||||
message: FormErrorMessages.minLength(8),
|
||||
},
|
||||
maxLength: {
|
||||
value: 128,
|
||||
message: FormErrorMessages.maxLength(128),
|
||||
},
|
||||
})}
|
||||
name="password"
|
||||
/>
|
||||
{errors.password && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.password.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<InputGroup
|
||||
label="Phone"
|
||||
placeholder="Phone"
|
||||
type="text"
|
||||
{...register("phone", {
|
||||
minLength: {
|
||||
value: 10,
|
||||
message: FormErrorMessages.minLength(10),
|
||||
},
|
||||
maxLength: {
|
||||
value: 20,
|
||||
message: FormErrorMessages.maxLength(20),
|
||||
},
|
||||
})}
|
||||
name="phone"
|
||||
/>
|
||||
{errors.phone && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.phone.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<InputGroup
|
||||
label="Department"
|
||||
placeholder="Department"
|
||||
type="text"
|
||||
{...register("department", {
|
||||
minLength: {
|
||||
value: 2,
|
||||
message: FormErrorMessages.minLength(2),
|
||||
},
|
||||
maxLength: {
|
||||
value: 100,
|
||||
message: FormErrorMessages.maxLength(100),
|
||||
},
|
||||
})}
|
||||
name="department"
|
||||
/>
|
||||
{errors.department && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.department.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<InputGroup
|
||||
label="Position"
|
||||
placeholder="Position"
|
||||
type="text"
|
||||
{...register("position", {
|
||||
minLength: {
|
||||
value: 2,
|
||||
message: FormErrorMessages.minLength(2),
|
||||
},
|
||||
maxLength: {
|
||||
value: 100,
|
||||
message: FormErrorMessages.maxLength(100),
|
||||
},
|
||||
})}
|
||||
name="position"
|
||||
/>
|
||||
{errors.position && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.position.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<InputGroup
|
||||
type="file"
|
||||
fileStyleVariant="style1"
|
||||
label="Attach file"
|
||||
placeholder="Attach file"
|
||||
register={register}
|
||||
name="profile_image"
|
||||
/>
|
||||
{errors.profile_image && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.profile_image.message as string}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-span-2 flex gap-6">
|
||||
<button
|
||||
type="submit"
|
||||
className="mt-6 flex w-fit justify-center rounded-lg bg-primary p-[13px] font-medium text-white hover:bg-opacity-90"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={router.back}
|
||||
className="mt-6 flex w-fit justify-center rounded-lg bg-error p-[13px] font-medium text-white hover:bg-opacity-90"
|
||||
>
|
||||
Opt out
|
||||
</button>
|
||||
</div>
|
||||
</ShowcaseSection>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateAdminForm;
|
||||
@@ -0,0 +1,49 @@
|
||||
import { useCreateAdmin, useUpdateAdmin } from "@/hooks/queries";
|
||||
import { ICreateAdminCredentials } from "@/lib/api";
|
||||
import { useIsMutating } from "@tanstack/react-query";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { SubmitHandler, useForm } from "react-hook-form";
|
||||
|
||||
interface IProps {
|
||||
editMode?: boolean;
|
||||
defaultValues?: ICreateAdminCredentials;
|
||||
id?: string;
|
||||
}
|
||||
const useCreateAdminPresenter = ({
|
||||
editMode = false,
|
||||
defaultValues,
|
||||
id,
|
||||
}: IProps) => {
|
||||
const isMutating = useIsMutating();
|
||||
const {
|
||||
register,
|
||||
formState: { errors },
|
||||
handleSubmit,
|
||||
watch,
|
||||
} = useForm<ICreateAdminCredentials>({
|
||||
mode: "onChange",
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
const { mutate: createAdmin } = useCreateAdmin();
|
||||
const { mutate: updateAdmin } = useUpdateAdmin(id as string);
|
||||
const onSubmit: SubmitHandler<ICreateAdminCredentials> = (data) => {
|
||||
if (editMode) {
|
||||
updateAdmin({ id: id as string, credentials: data });
|
||||
} else {
|
||||
createAdmin({ ...data, role: "admin" });
|
||||
}
|
||||
};
|
||||
const router = useRouter();
|
||||
return {
|
||||
errors,
|
||||
register,
|
||||
handleSubmit,
|
||||
router,
|
||||
isMutating,
|
||||
onSubmit,
|
||||
watchForm: watch,
|
||||
};
|
||||
};
|
||||
|
||||
export default useCreateAdminPresenter;
|
||||
@@ -0,0 +1,644 @@
|
||||
"use client";
|
||||
import { ICreateCompanyCredentials } from "@/lib/api/types/Company";
|
||||
import useCreateCompanyPresenter from "./useCreateCompanyPresenter";
|
||||
import { ShowcaseSection } from "../../Layouts/showcase-section";
|
||||
import { title } from "process";
|
||||
import InputGroup from "@/components/FormElements/InputGroup";
|
||||
import { FormErrorMessages } from "@/constants/Texts";
|
||||
import { Select } from "@/components/FormElements/select";
|
||||
import { CurrencyIcon, GlobeIcon, MoneyIcon } from "@/assets/icons";
|
||||
import MultiSelect from "@/components/FormElements/MultiSelect";
|
||||
import { TextAreaGroup } from "@/components/FormElements/InputGroup/text-area";
|
||||
|
||||
interface IProps {
|
||||
editMode?: boolean;
|
||||
defaultValues?: ICreateCompanyCredentials;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
const CreateCompany = ({ defaultValues, editMode, id }: IProps) => {
|
||||
const {
|
||||
errors,
|
||||
handleCancel,
|
||||
handleSubmit,
|
||||
isCreating,
|
||||
isMutating,
|
||||
isUpdating,
|
||||
onSubmit,
|
||||
router,
|
||||
register,
|
||||
} = useCreateCompanyPresenter({
|
||||
editMode,
|
||||
defaultValues,
|
||||
id,
|
||||
});
|
||||
return (
|
||||
<form
|
||||
className="grid grid-cols-1 items-start gap-9 rounded-xl bg-gray-1 p-10 sm:grid-cols-2"
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
>
|
||||
<div className="flex flex-col gap-9">
|
||||
<ShowcaseSection
|
||||
title="Company Information"
|
||||
className="flex flex-col gap-5"
|
||||
>
|
||||
<InputGroup
|
||||
{...register("name", {
|
||||
required: { message: FormErrorMessages.required, value: true },
|
||||
minLength: {
|
||||
value: 2,
|
||||
message: FormErrorMessages.minLength(2),
|
||||
},
|
||||
maxLength: {
|
||||
value: 200,
|
||||
message: FormErrorMessages.maxLength(200),
|
||||
},
|
||||
})}
|
||||
label="Name"
|
||||
name="name"
|
||||
type="text"
|
||||
placeholder="Enter Company Name"
|
||||
/>
|
||||
{errors.name && (
|
||||
<p className="mt-1 text-sm text-red-500">{errors.name.message}</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("email", {
|
||||
required: { message: FormErrorMessages.required, value: true },
|
||||
pattern: {
|
||||
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
||||
message: FormErrorMessages.invalidEmail,
|
||||
},
|
||||
})}
|
||||
label="Email"
|
||||
name="email"
|
||||
type="email"
|
||||
placeholder="Enter Company Email"
|
||||
/>
|
||||
{errors.email && (
|
||||
<p className="mt-1 text-sm text-red-500">{errors.email.message}</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("phone", {
|
||||
required: { message: FormErrorMessages.required, value: true },
|
||||
minLength: {
|
||||
value: 10,
|
||||
message: FormErrorMessages.minLength(10),
|
||||
},
|
||||
maxLength: {
|
||||
value: 20,
|
||||
message: FormErrorMessages.maxLength(20),
|
||||
},
|
||||
})}
|
||||
label="Phone"
|
||||
name="phone"
|
||||
type="tel"
|
||||
placeholder="Enter Company Phone"
|
||||
/>
|
||||
{errors.phone && (
|
||||
<p className="mt-1 text-sm text-red-500">{errors.phone.message}</p>
|
||||
)}
|
||||
<Select
|
||||
{...register("type", {
|
||||
required: { message: FormErrorMessages.required, value: true },
|
||||
})}
|
||||
name="type"
|
||||
label="Company type"
|
||||
items={[
|
||||
{ value: "private", label: "Private" },
|
||||
{ value: "public", label: "Public" },
|
||||
{ value: "government", label: "Government" },
|
||||
{ value: "ngo", label: "NGO" },
|
||||
{ value: "startup", label: "Startup" },
|
||||
]}
|
||||
defaultValue="Private"
|
||||
prefixIcon={<GlobeIcon />}
|
||||
/>
|
||||
{errors.type && (
|
||||
<p className="mt-1 text-sm text-red-500">{errors.type.message}</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("registration_number", {
|
||||
required: { message: FormErrorMessages.required, value: true },
|
||||
minLength: { value: 5, message: FormErrorMessages.minLength(5) },
|
||||
maxLength: {
|
||||
value: 50,
|
||||
message: FormErrorMessages.maxLength(50),
|
||||
},
|
||||
})}
|
||||
label="Registration Number"
|
||||
name="registration_number"
|
||||
type="text"
|
||||
placeholder="Enter Registration Number"
|
||||
/>
|
||||
{errors.registration_number && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.registration_number.message}
|
||||
</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("tax_id", {
|
||||
required: { message: FormErrorMessages.required, value: true },
|
||||
minLength: { value: 5, message: FormErrorMessages.minLength(5) },
|
||||
maxLength: {
|
||||
value: 50,
|
||||
message: FormErrorMessages.maxLength(50),
|
||||
},
|
||||
})}
|
||||
label="Tax id"
|
||||
name="tax_id"
|
||||
type="text"
|
||||
placeholder="Enter Tax id"
|
||||
/>
|
||||
{errors.tax_id && (
|
||||
<p className="mt-1 text-sm text-red-500">{errors.tax_id.message}</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("industry", {
|
||||
required: { message: FormErrorMessages.required, value: true },
|
||||
minLength: { value: 2, message: FormErrorMessages.minLength(2) },
|
||||
maxLength: {
|
||||
value: 100,
|
||||
message: FormErrorMessages.maxLength(100),
|
||||
},
|
||||
})}
|
||||
label="Industry"
|
||||
name="industry"
|
||||
type="text"
|
||||
placeholder="Enter Industry"
|
||||
/>
|
||||
{errors.industry && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.industry.message}
|
||||
</p>
|
||||
)}
|
||||
<TextAreaGroup
|
||||
label="Description"
|
||||
{...register("description", {
|
||||
minLength: {
|
||||
value: 10,
|
||||
message: FormErrorMessages.minLength(10),
|
||||
},
|
||||
maxLength: {
|
||||
value: 1000,
|
||||
message: FormErrorMessages.maxLength(1000),
|
||||
},
|
||||
})}
|
||||
name="description"
|
||||
placeholder="Enter Description"
|
||||
/>
|
||||
{errors.description && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("website", {
|
||||
required: { message: FormErrorMessages.required, value: true },
|
||||
minLength: { value: 2, message: FormErrorMessages.minLength(2) },
|
||||
maxLength: {
|
||||
value: 100,
|
||||
message: FormErrorMessages.maxLength(100),
|
||||
},
|
||||
})}
|
||||
label="Website"
|
||||
name="website"
|
||||
type="url"
|
||||
placeholder="Enter Company Website"
|
||||
/>
|
||||
{errors.website && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.website.message}
|
||||
</p>
|
||||
)}
|
||||
</ShowcaseSection>
|
||||
<ShowcaseSection
|
||||
title="Contact Person"
|
||||
className="grid grid-cols-1 gap-5"
|
||||
>
|
||||
<InputGroup
|
||||
{...register("contact_person.full_name", {
|
||||
required: { value: true, message: FormErrorMessages.required },
|
||||
minLength: {
|
||||
value: 2,
|
||||
message: FormErrorMessages.minLength(2),
|
||||
},
|
||||
maxLength: {
|
||||
value: 100,
|
||||
message: FormErrorMessages.maxLength(100),
|
||||
},
|
||||
})}
|
||||
label="Full name"
|
||||
name="contact_person.full_name"
|
||||
placeholder="Enter Full name"
|
||||
type="text"
|
||||
/>
|
||||
{errors.contact_person?.full_name && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.contact_person.full_name.message}
|
||||
</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("contact_person.position", {
|
||||
required: { value: true, message: FormErrorMessages.required },
|
||||
minLength: {
|
||||
value: 2,
|
||||
message: FormErrorMessages.minLength(2),
|
||||
},
|
||||
maxLength: {
|
||||
value: 100,
|
||||
message: FormErrorMessages.maxLength(100),
|
||||
},
|
||||
})}
|
||||
label="Position"
|
||||
name="contact_person.position"
|
||||
placeholder="Enter Content Person Position"
|
||||
type="text"
|
||||
/>
|
||||
{errors.contact_person?.position && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.contact_person.position.message}
|
||||
</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("contact_person.email", {
|
||||
required: { value: true, message: FormErrorMessages.required },
|
||||
pattern: {
|
||||
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
||||
message: FormErrorMessages.invalidEmail,
|
||||
},
|
||||
})}
|
||||
label="Email"
|
||||
name="contact_person.email"
|
||||
placeholder="Enter Contact Person Email"
|
||||
type="email"
|
||||
/>
|
||||
{errors.contact_person?.email && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.contact_person.email.message}
|
||||
</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("contact_person.phone", {
|
||||
required: { value: true, message: FormErrorMessages.required },
|
||||
minLength: {
|
||||
value: 10,
|
||||
message: FormErrorMessages.minLength(10),
|
||||
},
|
||||
maxLength: {
|
||||
value: 20,
|
||||
message: FormErrorMessages.maxLength(20),
|
||||
},
|
||||
})}
|
||||
label="Phone"
|
||||
name="contact_person.phone"
|
||||
placeholder="Enter Contact Person Phone"
|
||||
type="tel"
|
||||
/>
|
||||
{errors.contact_person?.phone && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.contact_person.phone.message}
|
||||
</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("contact_person.mobile", {
|
||||
minLength: {
|
||||
value: 10,
|
||||
message: FormErrorMessages.minLength(10),
|
||||
},
|
||||
maxLength: {
|
||||
value: 20,
|
||||
message: FormErrorMessages.maxLength(20),
|
||||
},
|
||||
})}
|
||||
label="Mobile"
|
||||
name="contact_person.mobile"
|
||||
placeholder="Enter Contact Person Mobile"
|
||||
type="tel"
|
||||
/>
|
||||
{errors.contact_person?.mobile && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.contact_person.mobile.message}
|
||||
</p>
|
||||
)}
|
||||
</ShowcaseSection>
|
||||
<ShowcaseSection title="Settings" className="grid grid-cols-1 gap-5">
|
||||
<Select
|
||||
{...register("language")}
|
||||
name="language"
|
||||
label="Language"
|
||||
items={[
|
||||
{ value: "en", label: "EN" },
|
||||
{ value: "ar", label: "AR" },
|
||||
{ value: "fr", label: "FR" },
|
||||
{ value: "es", label: "ES" },
|
||||
{ value: "de", label: "DE" },
|
||||
{ value: "zh", label: "ZH" },
|
||||
{ value: "jEna", label: "jEna" },
|
||||
{ value: "ko", label: "KO" },
|
||||
]}
|
||||
defaultValue="en"
|
||||
prefixIcon={<GlobeIcon />}
|
||||
/>
|
||||
{errors.language && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.language.message}
|
||||
</p>
|
||||
)}
|
||||
<Select
|
||||
{...register("currency")}
|
||||
name="currency"
|
||||
label="Currency"
|
||||
items={[
|
||||
{ label: "USD", value: "USD" },
|
||||
{ label: "EUR", value: "EUR" },
|
||||
{ label: "GBP", value: "GBP" },
|
||||
{ label: "JPY", value: "JPY" },
|
||||
{ label: "CAD", value: "CAD" },
|
||||
{ label: "AUD", value: "AUD" },
|
||||
{ label: "CHF", value: "CHF" },
|
||||
{ label: "CNY", value: "CNY" },
|
||||
{ label: "INR", value: "INR" },
|
||||
{ label: "BRL", value: "BRL" },
|
||||
]}
|
||||
defaultValue="en"
|
||||
prefixIcon={<MoneyIcon />}
|
||||
/>
|
||||
{errors.currency && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.currency.message}
|
||||
</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("timezone", {
|
||||
minLength: { value: 3, message: FormErrorMessages.minLength(3) },
|
||||
maxLength: {
|
||||
value: 50,
|
||||
message: FormErrorMessages.maxLength(50),
|
||||
},
|
||||
})}
|
||||
label="Timezone"
|
||||
name="timezone"
|
||||
type="text"
|
||||
placeholder="Enter Timezone"
|
||||
/>
|
||||
{errors.timezone && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.timezone.message}
|
||||
</p>
|
||||
)}
|
||||
</ShowcaseSection>
|
||||
</div>
|
||||
<div className="flex flex-col gap-9">
|
||||
<ShowcaseSection
|
||||
title="Business Information"
|
||||
className="grid grid-cols-1 gap-5"
|
||||
>
|
||||
<InputGroup
|
||||
{...register("employee_count", {
|
||||
min: { value: 1, message: FormErrorMessages.min(1) },
|
||||
max: { value: 100000, message: FormErrorMessages.max(100000) },
|
||||
})}
|
||||
label="Employee Count"
|
||||
name="employee_count"
|
||||
type="number"
|
||||
placeholder="Enter Employee Count"
|
||||
/>
|
||||
{errors.employee_count && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.employee_count.message}
|
||||
</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("annual_revenue", {
|
||||
min: { value: 0, message: FormErrorMessages.min(0) },
|
||||
max: {
|
||||
value: 999999999999,
|
||||
message: FormErrorMessages.max(999999999999),
|
||||
},
|
||||
})}
|
||||
label="Annual Revenue"
|
||||
name="annual_revenue"
|
||||
type="number"
|
||||
placeholder="Enter Annual Revenue"
|
||||
/>
|
||||
{errors.annual_revenue && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.annual_revenue.message}
|
||||
</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("founded_year", {
|
||||
min: { value: 1800, message: FormErrorMessages.min(1800) },
|
||||
max: {
|
||||
value: 2100,
|
||||
message: FormErrorMessages.max(2100),
|
||||
},
|
||||
})}
|
||||
label="Founded Year"
|
||||
name="founded_year"
|
||||
type="number"
|
||||
placeholder="Enter Founded Year"
|
||||
/>
|
||||
{errors.founded_year && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.founded_year.message}
|
||||
</p>
|
||||
)}
|
||||
</ShowcaseSection>
|
||||
<ShowcaseSection title="Address" className="grid grid-cols-1 gap-5">
|
||||
<InputGroup
|
||||
{...register("address.street", {
|
||||
required: { message: FormErrorMessages.required, value: true },
|
||||
minLength: {
|
||||
value: 5,
|
||||
message: FormErrorMessages.minLength(5),
|
||||
},
|
||||
maxLength: {
|
||||
value: 200,
|
||||
message: FormErrorMessages.maxLength(200),
|
||||
},
|
||||
})}
|
||||
label="Street"
|
||||
name="address.street"
|
||||
type="text"
|
||||
placeholder="Enter Street"
|
||||
/>
|
||||
{errors.address?.street && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.address.street.message}
|
||||
</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("address.city", {
|
||||
required: { message: FormErrorMessages.required, value: true },
|
||||
minLength: {
|
||||
value: 2,
|
||||
message: FormErrorMessages.minLength(2),
|
||||
},
|
||||
maxLength: {
|
||||
value: 100,
|
||||
message: FormErrorMessages.maxLength(100),
|
||||
},
|
||||
})}
|
||||
label="City"
|
||||
name="address.city"
|
||||
type="text"
|
||||
placeholder="Enter City"
|
||||
/>
|
||||
{errors.address?.city && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.address.city.message}
|
||||
</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("address.state", {
|
||||
required: { message: FormErrorMessages.required, value: true },
|
||||
minLength: {
|
||||
value: 2,
|
||||
message: FormErrorMessages.minLength(2),
|
||||
},
|
||||
maxLength: {
|
||||
value: 100,
|
||||
message: FormErrorMessages.maxLength(100),
|
||||
},
|
||||
})}
|
||||
label="State"
|
||||
name="address.state"
|
||||
type="text"
|
||||
placeholder="Enter State"
|
||||
/>
|
||||
{errors.address?.state && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.address.state.message}
|
||||
</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("address.postal_code", {
|
||||
required: { message: FormErrorMessages.required, value: true },
|
||||
minLength: {
|
||||
value: 3,
|
||||
message: FormErrorMessages.minLength(3),
|
||||
},
|
||||
maxLength: {
|
||||
value: 20,
|
||||
message: FormErrorMessages.maxLength(20),
|
||||
},
|
||||
})}
|
||||
label="Postal code"
|
||||
name="address.postal_code"
|
||||
type="text"
|
||||
placeholder="Enter Postal code"
|
||||
/>
|
||||
{errors.address?.postal_code && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.address.postal_code.message}
|
||||
</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("address.country", {
|
||||
required: { message: FormErrorMessages.required, value: true },
|
||||
minLength: {
|
||||
value: 2,
|
||||
message: FormErrorMessages.minLength(2),
|
||||
},
|
||||
maxLength: {
|
||||
value: 100,
|
||||
message: FormErrorMessages.maxLength(100),
|
||||
},
|
||||
})}
|
||||
label="Country"
|
||||
name="address.country"
|
||||
type="text"
|
||||
placeholder="Enter Country"
|
||||
/>
|
||||
{errors.address?.country && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.address.country.message}
|
||||
</p>
|
||||
)}
|
||||
</ShowcaseSection>
|
||||
<ShowcaseSection title="Tags" className="grid grid-cols-1 gap-5">
|
||||
<InputGroup
|
||||
{...register("tags.keywords")}
|
||||
label="Keywords"
|
||||
name="tags.keywords"
|
||||
type="text"
|
||||
placeholder="Enter Tag Keywords"
|
||||
/>
|
||||
{errors.tags?.keywords && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.tags.keywords.message}
|
||||
</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("tags.categories")}
|
||||
label="Categories"
|
||||
name="tags.categories"
|
||||
type="text"
|
||||
placeholder="Enter Tag Categories"
|
||||
/>
|
||||
{errors.tags?.categories && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.tags.categories.message}
|
||||
</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("tags.specializations")}
|
||||
label="Specializations"
|
||||
name="tags.specializations"
|
||||
type="text"
|
||||
placeholder="Enter Tag Specializations"
|
||||
/>
|
||||
{errors.tags?.specializations && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.tags.specializations.message}
|
||||
</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("tags.certifications")}
|
||||
label="Certifications"
|
||||
name="tags.certifications"
|
||||
type="text"
|
||||
placeholder="Enter Tag Certifications"
|
||||
/>
|
||||
{errors.tags?.certifications && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.tags.certifications.message}
|
||||
</p>
|
||||
)}
|
||||
<InputGroup
|
||||
{...register("tags.cpv_codes")}
|
||||
label="CPV Code"
|
||||
name="tags.cpv_codes"
|
||||
type="text"
|
||||
placeholder="Enter Tag CPV Code"
|
||||
/>
|
||||
{errors.tags?.cpv_codes && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.tags.cpv_codes.message}
|
||||
</p>
|
||||
)}
|
||||
</ShowcaseSection>
|
||||
</div>
|
||||
|
||||
<div className="col-span-2 flex gap-6">
|
||||
<button
|
||||
type="submit"
|
||||
className="mt-6 flex w-fit justify-center rounded-lg bg-primary p-[13px] font-medium text-white hover:bg-opacity-90"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={router.back}
|
||||
className="mt-6 flex w-fit justify-center rounded-lg bg-error p-[13px] font-medium text-white hover:bg-opacity-90"
|
||||
>
|
||||
Opt out
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateCompany;
|
||||
@@ -0,0 +1,87 @@
|
||||
"use client";
|
||||
import { useCreateCompany, useUpdateCompany } from "@/hooks/queries";
|
||||
import { ICreateCompanyCredentials } from "@/lib/api";
|
||||
import { useIsMutating } from "@tanstack/react-query";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { SubmitHandler, useForm } from "react-hook-form";
|
||||
|
||||
interface IProps {
|
||||
editMode?: boolean;
|
||||
defaultValues?: ICreateCompanyCredentials;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
const useCreateCompanyPresenter = ({ defaultValues, editMode, id }: IProps) => {
|
||||
const isMutating = useIsMutating();
|
||||
const {
|
||||
register,
|
||||
formState: { errors },
|
||||
handleSubmit,
|
||||
reset,
|
||||
} = useForm<ICreateCompanyCredentials>({ mode: "onChange", defaultValues });
|
||||
const router = useRouter();
|
||||
const { mutate: createCompany, isPending: isCreating } = useCreateCompany();
|
||||
const { mutate: updateCompany, isPending: isUpdating } = useUpdateCompany(
|
||||
id as string,
|
||||
);
|
||||
const onSubmit: SubmitHandler<ICreateCompanyCredentials> = (data) => {
|
||||
const formattedData = {
|
||||
...data,
|
||||
founded_year: data.founded_year ? +data.founded_year : 0,
|
||||
employee_count: data.employee_count ? +data.employee_count : 0,
|
||||
annual_revenue: data.annual_revenue ? +data.annual_revenue : 0,
|
||||
tags: {
|
||||
keywords: data.tags?.keywords
|
||||
? (data.tags.keywords as unknown as string)
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
: [],
|
||||
categories: data.tags?.categories
|
||||
? (data.tags.categories as unknown as string)
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
: [],
|
||||
specializations: data.tags?.specializations
|
||||
? (data.tags.specializations as unknown as string)
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
: [],
|
||||
certifications: data.tags?.certifications
|
||||
? (data.tags.certifications as unknown as string)
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
: [],
|
||||
cpv_codes: data.tags?.cpv_codes
|
||||
? (data.tags.cpv_codes as unknown as string)
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
: [],
|
||||
},
|
||||
};
|
||||
if (editMode) {
|
||||
updateCompany({
|
||||
id: id as string,
|
||||
credentials: formattedData as ICreateCompanyCredentials,
|
||||
});
|
||||
} else {
|
||||
createCompany(formattedData as ICreateCompanyCredentials);
|
||||
}
|
||||
};
|
||||
const handleCancel = () => {
|
||||
reset();
|
||||
router.back();
|
||||
};
|
||||
return {
|
||||
errors,
|
||||
register,
|
||||
handleSubmit,
|
||||
onSubmit,
|
||||
handleCancel,
|
||||
isMutating,
|
||||
isCreating,
|
||||
isUpdating,
|
||||
router
|
||||
};
|
||||
};
|
||||
|
||||
export default useCreateCompanyPresenter;
|
||||
@@ -0,0 +1,193 @@
|
||||
"use client";
|
||||
|
||||
import { CreateCustomerCredentials } from "@/lib/api";
|
||||
import useCreateCustomerPresenter from "./useCreateCustomerPresenter";
|
||||
import { ShowcaseSection } from "../../Layouts/showcase-section";
|
||||
import InputGroup from "@/components/FormElements/InputGroup";
|
||||
import { FormErrorMessages } from "@/constants/Texts";
|
||||
import { Select } from "@/components/FormElements/select";
|
||||
import MultiSelect from "@/components/FormElements/MultiSelect";
|
||||
|
||||
interface IProps {
|
||||
editMode?: boolean;
|
||||
defaultValues?: CreateCustomerCredentials;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
const CreateCustomer = ({ defaultValues, editMode, id }: IProps) => {
|
||||
const { errors, handleSubmit, register, onSubmit, companies, router } =
|
||||
useCreateCustomerPresenter({ defaultValues, editMode, id });
|
||||
|
||||
return (
|
||||
<form className="grid grid-cols-1 gap-9" onSubmit={handleSubmit(onSubmit)}>
|
||||
<ShowcaseSection
|
||||
title="Create Customer"
|
||||
className="grid grid-cols-2 gap-5"
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
<InputGroup
|
||||
{...register("username", {
|
||||
required: { value: true, message: FormErrorMessages.required },
|
||||
minLength: { value: 3, message: FormErrorMessages.minLength(3) },
|
||||
maxLength: {
|
||||
value: 30,
|
||||
message: FormErrorMessages.maxLength(30),
|
||||
},
|
||||
pattern: {
|
||||
value: /^[a-zA-Z0-9]+$/,
|
||||
message: FormErrorMessages.alphaNumeric,
|
||||
},
|
||||
})}
|
||||
name="username"
|
||||
label="Username"
|
||||
type="text"
|
||||
placeholder="Enter username"
|
||||
/>
|
||||
{errors.username && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.username.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<MultiSelect
|
||||
label="Select companies"
|
||||
items={
|
||||
companies
|
||||
? companies?.map((item) => ({
|
||||
value: item.id,
|
||||
text: item.name,
|
||||
}))
|
||||
: []
|
||||
}
|
||||
placeholder="Choose your interests..."
|
||||
{...register("company_ids", {
|
||||
required: "Please select at least one interest",
|
||||
})}
|
||||
errors={errors}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<InputGroup
|
||||
{...register("email", {
|
||||
required: { value: true, message: FormErrorMessages.required },
|
||||
pattern: {
|
||||
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
||||
message: FormErrorMessages.invalidEmail,
|
||||
},
|
||||
})}
|
||||
name="email"
|
||||
label="Email"
|
||||
type="email"
|
||||
placeholder="Enter email"
|
||||
/>
|
||||
{errors.email && (
|
||||
<p className="mt-1 text-sm text-red-500">{errors.email.message}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<InputGroup
|
||||
{...register("password", {
|
||||
required: { value: true, message: FormErrorMessages.required },
|
||||
minLength: { value: 8, message: FormErrorMessages.minLength(8) },
|
||||
maxLength: {
|
||||
value: 128,
|
||||
message: FormErrorMessages.maxLength(128),
|
||||
},
|
||||
})}
|
||||
name="password"
|
||||
label="Password"
|
||||
type="password"
|
||||
placeholder="Enter password"
|
||||
/>
|
||||
{errors.password && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.password.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<InputGroup
|
||||
{...register("full_name", {
|
||||
minLength: { value: 2, message: FormErrorMessages.minLength(2) },
|
||||
maxLength: {
|
||||
value: 100,
|
||||
message: FormErrorMessages.maxLength(100),
|
||||
},
|
||||
})}
|
||||
name="full_name"
|
||||
label="Full name"
|
||||
type="full_name"
|
||||
placeholder="Enter full name"
|
||||
/>
|
||||
{errors.full_name && (
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
{errors.full_name.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<InputGroup
|
||||
{...register("phone", {
|
||||
minLength: {
|
||||
value: 10,
|
||||
message: FormErrorMessages.minLength(10),
|
||||
},
|
||||
maxLength: {
|
||||
value: 20,
|
||||
message: FormErrorMessages.maxLength(20),
|
||||
},
|
||||
})}
|
||||
name="phone"
|
||||
label="Phone"
|
||||
type="tel"
|
||||
placeholder="Enter phone"
|
||||
/>
|
||||
{errors.phone && (
|
||||
<p className="mt-1 text-sm text-red-500">{errors.phone.message}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<InputGroup
|
||||
{...register("mobile", {
|
||||
minLength: {
|
||||
value: 10,
|
||||
message: FormErrorMessages.minLength(10),
|
||||
},
|
||||
maxLength: {
|
||||
value: 20,
|
||||
message: FormErrorMessages.maxLength(20),
|
||||
},
|
||||
})}
|
||||
name="mobile"
|
||||
label="Mobile"
|
||||
type="tel"
|
||||
placeholder="Enter mobile"
|
||||
/>
|
||||
{errors.mobile && (
|
||||
<p className="mt-1 text-sm text-red-500">{errors.mobile.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-span-2 flex gap-6">
|
||||
<button
|
||||
type="submit"
|
||||
className="mt-6 flex w-fit justify-center rounded-lg bg-primary p-[13px] font-medium text-white hover:bg-opacity-90"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={router.back}
|
||||
className="mt-6 flex w-fit justify-center rounded-lg bg-error p-[13px] font-medium text-white hover:bg-opacity-90"
|
||||
>
|
||||
Opt out
|
||||
</button>
|
||||
</div>
|
||||
</ShowcaseSection>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateCustomer;
|
||||
@@ -0,0 +1,64 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
useCompanyFullList,
|
||||
useCreateCustomer,
|
||||
useUpdateCustomer,
|
||||
} from "@/hooks/queries";
|
||||
import { CreateCustomerCredentials } from "@/lib/api";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { SubmitHandler, useForm } from "react-hook-form";
|
||||
|
||||
interface IProps {
|
||||
editMode?: boolean;
|
||||
defaultValues?: CreateCustomerCredentials;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
const useCreateCustomerPresenter = ({
|
||||
defaultValues,
|
||||
editMode,
|
||||
id,
|
||||
}: IProps) => {
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm<CreateCustomerCredentials>({
|
||||
mode: "onChange",
|
||||
defaultValues,
|
||||
});
|
||||
const router = useRouter();
|
||||
const { mutate: createCustomer, isPending: isCreating } = useCreateCustomer();
|
||||
const { mutate: updateCustomer, isPending: isUpdating } = useUpdateCustomer(
|
||||
id as string,
|
||||
);
|
||||
const { data: companies } = useCompanyFullList();
|
||||
const onSubmit: SubmitHandler<CreateCustomerCredentials> = (data) => {
|
||||
if (editMode) {
|
||||
updateCustomer({
|
||||
id: id as string,
|
||||
credentials: data,
|
||||
});
|
||||
} else {
|
||||
console.log(data.company_ids);
|
||||
|
||||
createCustomer({
|
||||
...data,
|
||||
employee_count: data.employee_count ? +data.employee_count : undefined,
|
||||
annual_revenue: data.annual_revenue ? +data.annual_revenue : undefined,
|
||||
founded_year: data.founded_year ? +data.founded_year : undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
return {
|
||||
handleSubmit,
|
||||
register,
|
||||
errors,
|
||||
onSubmit,
|
||||
companies: companies?.data.companies,
|
||||
router,
|
||||
};
|
||||
};
|
||||
|
||||
export default useCreateCustomerPresenter;
|
||||
Reference in New Issue
Block a user