Adding New dropdown in across all modules.

This commit is contained in:
Kartik Sharma 2025-12-05 17:57:08 +05:30
parent 7df9d47f07
commit dbf4f5e9c8
12 changed files with 869 additions and 681 deletions

View File

@ -396,34 +396,33 @@ const ManageContact = ({ contactId, closeModal }) => {
{/* Category + Projects */} {/* Category + Projects */}
<div className="row"> <div className="row">
<div className="col-md-6 text-start"> <div className="col-md-6 text-start">
<label className="form-label">Category</label> <AppFormController
<select name="contactCategoryId"
className="form-select " control={control}
{...register("contactCategoryId")} rules={{ required: "Category is required" }}
> render={({ field }) => (
{contactCategoryLoading && !contactCategory ? ( <SelectField
<option disabled value=""> label="Category"
Loading... required
</option> options={contactCategory ?? []}
) : ( placeholder="Select Category"
<> labelKeyKey="name"
<option disabled value=""> valueKeyKey="id"
Select Category value={field.value}
</option> onChange={field.onChange}
{contactCategory?.map((cate) => ( isLoading={contactCategoryLoading && !contactCategory}
<option key={cate.id} value={cate.id}> className="m-0"
{cate.name} />
</option>
))}
</>
)} )}
</select> />
{errors.contactCategoryId && ( {errors.contactCategoryId && (
<small className="danger-text"> <small className="danger-text">
{errors.contactCategoryId.message} {errors.contactCategoryId.message}
</small> </small>
)} )}
</div> </div>
<div className="col-12 col-md-6 text-start"> <div className="col-12 col-md-6 text-start">
<SelectMultiple <SelectMultiple
name="projectIds" name="projectIds"

View File

@ -17,6 +17,8 @@ import DatePicker from "../common/DatePicker";
import { defatEmployeeObj, employeeSchema } from "./EmployeeSchema"; import { defatEmployeeObj, employeeSchema } from "./EmployeeSchema";
import { useOrganizationsList } from "../../hooks/useOrganization"; import { useOrganizationsList } from "../../hooks/useOrganization";
import { ITEMS_PER_PAGE } from "../../utils/constants"; import { ITEMS_PER_PAGE } from "../../utils/constants";
import { AppFormController } from "../../hooks/appHooks/useAppForm";
import SelectField from "../common/Forms/SelectField";
const ManageEmployee = ({ employeeId, onClosed }) => { const ManageEmployee = ({ employeeId, onClosed }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -96,26 +98,26 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
reset( reset(
currentEmployee currentEmployee
? { ? {
id: currentEmployee.id || null, id: currentEmployee.id || null,
firstName: currentEmployee.firstName || "", firstName: currentEmployee.firstName || "",
middleName: currentEmployee.middleName || "", middleName: currentEmployee.middleName || "",
lastName: currentEmployee.lastName || "", lastName: currentEmployee.lastName || "",
email: currentEmployee.email || "", email: currentEmployee.email || "",
currentAddress: currentEmployee.currentAddress || "", currentAddress: currentEmployee.currentAddress || "",
birthDate: formatDate(currentEmployee.birthDate) || "", birthDate: formatDate(currentEmployee.birthDate) || "",
joiningDate: formatDate(currentEmployee.joiningDate) || "", joiningDate: formatDate(currentEmployee.joiningDate) || "",
emergencyPhoneNumber: currentEmployee.emergencyPhoneNumber || "", emergencyPhoneNumber: currentEmployee.emergencyPhoneNumber || "",
emergencyContactPerson: emergencyContactPerson:
currentEmployee.emergencyContactPerson || "", currentEmployee.emergencyContactPerson || "",
aadharNumber: currentEmployee.aadharNumber || "", aadharNumber: currentEmployee.aadharNumber || "",
gender: currentEmployee.gender || "", gender: currentEmployee.gender || "",
panNumber: currentEmployee.panNumber || "", panNumber: currentEmployee.panNumber || "",
permanentAddress: currentEmployee.permanentAddress || "", permanentAddress: currentEmployee.permanentAddress || "",
phoneNumber: currentEmployee.phoneNumber || "", phoneNumber: currentEmployee.phoneNumber || "",
jobRoleId: currentEmployee.jobRoleId?.toString() || "", jobRoleId: currentEmployee.jobRoleId?.toString() || "",
organizationId: currentEmployee.organizationId || "", organizationId: currentEmployee.organizationId || "",
hasApplicationAccess: currentEmployee.hasApplicationAccess || false, hasApplicationAccess: currentEmployee.hasApplicationAccess || false,
} }
: {} : {}
); );
setCurrentAddressLength(currentEmployee?.currentAddress?.length || 0); setCurrentAddressLength(currentEmployee?.currentAddress?.length || 0);
@ -147,7 +149,7 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
message: "Only letters are allowed", message: "Only letters are allowed",
}, },
})} })}
className="form-control form-control-sm" className="form-control "
id="firstName" id="firstName"
placeholder="First Name" placeholder="First Name"
onInput={(e) => { onInput={(e) => {
@ -173,7 +175,7 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
message: "Only letters are allowed", message: "Only letters are allowed",
}, },
})} })}
className="form-control form-control-sm" className="form-control "
id="middleName" id="middleName"
placeholder="Middle Name" placeholder="Middle Name"
onInput={(e) => { onInput={(e) => {
@ -201,7 +203,7 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
message: "Only letters are allowed", message: "Only letters are allowed",
}, },
})} })}
className="form-control form-control-sm" className="form-control "
id="lastName" id="lastName"
placeholder="Last Name" placeholder="Last Name"
onInput={(e) => { onInput={(e) => {
@ -231,7 +233,7 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
type="email" type="email"
id="email" id="email"
{...register("email")} {...register("email")}
className="form-control form-control-sm" className="form-control "
placeholder="example@domain.com" placeholder="example@domain.com"
maxLength={80} maxLength={80}
aria-describedby="Email" aria-describedby="Email"
@ -255,7 +257,7 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
keyboardType="numeric" keyboardType="numeric"
id="phoneNumber" id="phoneNumber"
{...register("phoneNumber")} {...register("phoneNumber")}
className="form-control form-control-sm" className="form-control "
placeholder="Phone Number" placeholder="Phone Number"
inputMode="numeric" inputMode="numeric"
maxLength={10} maxLength={10}
@ -272,7 +274,7 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
</div> </div>
<div className="row mb-3"></div> <div className="row mb-3"></div>
<div className="row mb-3"> <div className="row mb-3">
<div className="col-sm-4"> {/* <div className="col-sm-4">
<Label className="form-text text-start" required> <Label className="form-text text-start" required>
Gender Gender
</Label> </Label>
@ -300,7 +302,44 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
{errors.gender.message} {errors.gender.message}
</div> </div>
)} )}
</div> */}
<div className="col-sm-4">
<Label className="form-text text-start" required>
Gender
</Label>
<div className="">
<AppFormController
name="gender"
control={control}
render={({ field }) => (
<SelectField
label=""
options={[
{ id: "Male", name: "Male" },
{ id: "Female", name: "Female" },
{ id: "Other", name: "Other" },
]}
placeholder="Select Gender"
required
labelKey="name"
valueKey="id"
value={field.value}
onChange={field.onChange}
/>
)}
/>
</div>
{errors.gender && (
<div className="danger-text text-start" style={{ fontSize: "12px" }}>
{errors.gender.message}
</div>
)}
</div> </div>
<div className="col-sm-4"> <div className="col-sm-4">
<Label className="form-text text-start" required> <Label className="form-text text-start" required>
Birth Date Birth Date
@ -358,7 +397,7 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
<textarea <textarea
id="currentAddress" id="currentAddress"
className="form-control form-control-sm" className="form-control "
placeholder="Current Address" placeholder="Current Address"
aria-label="Current Address" aria-label="Current Address"
aria-describedby="basic-icon-default-message2" aria-describedby="basic-icon-default-message2"
@ -388,7 +427,7 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
<textarea <textarea
id="permanentAddress" id="permanentAddress"
className="form-control form-control-sm" className="form-control "
placeholder="Permanent Address" placeholder="Permanent Address"
aria-label="Permanent Address" aria-label="Permanent Address"
aria-describedby="basic-icon-default-message2" aria-describedby="basic-icon-default-message2"
@ -419,25 +458,34 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
<Label className="form-text text-start" required> <Label className="form-text text-start" required>
Organization Organization
</Label> </Label>
<div className="input-group">
<select <div>
className="form-select form-select-sm" <AppFormController
{...register("organizationId")} name="organizationId"
id="organizationId" control={control}
aria-label="" render={({ field }) => (
> <SelectField
<option disabled value=""> label="" // Already showing label above
Select Organization options={
</option> organzationList?.data
{organzationList?.data ?.sort((a, b) => a?.name?.localeCompare(b?.name))
.sort((a, b) => a?.name?.localeCompare(b?.name)) ?.map((item) => ({
.map((item) => ( id: item.id,
<option value={item?.id} key={item?.id}> name: item.name,
{item?.name} })) || []
</option> }
))} placeholder="Select Organization"
</select> required
labelKey="name"
valueKey="id"
value={field.value}
onChange={field.onChange}
className="m-0 form-select-sm w-100"
/>
)}
/>
</div> </div>
{errors.organizationId && ( {errors.organizationId && (
<div <div
className="danger-text text-start justify-content-center" className="danger-text text-start justify-content-center"
@ -448,6 +496,7 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
)} )}
</div> </div>
<div className="col-sm-6 d-flex align-items-center mt-2"> <div className="col-sm-6 d-flex align-items-center mt-2">
<label className="form-check-label d-flex align-items-center"> <label className="form-check-label d-flex align-items-center">
<input <input
@ -474,7 +523,7 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
</Label> </Label>
<div className="input-group"> <div className="input-group">
<select <select
className="form-select form-select-sm" className="form-select"
{...register("jobRoleId")} {...register("jobRoleId")}
id="jobRoleId" id="jobRoleId"
aria-label="" aria-label=""
@ -507,7 +556,7 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
<input <input
type="text" type="text"
{...register("emergencyContactPerson")} {...register("emergencyContactPerson")}
className="form-control form-control-sm" className="form-control "
id="emergencyContactPerson" id="emergencyContactPerson"
maxLength={50} maxLength={50}
placeholder="Contact Person" placeholder="Contact Person"
@ -528,7 +577,7 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
<input <input
type="text" type="text"
{...register("emergencyPhoneNumber")} {...register("emergencyPhoneNumber")}
className="form-control form-control-sm phone-mask" className="form-control phone-mask"
id="emergencyPhoneNumber" id="emergencyPhoneNumber"
placeholder="Phone Number" placeholder="Phone Number"
inputMode="numeric" inputMode="numeric"
@ -551,7 +600,7 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
<input <input
type="text" type="text"
{...register("aadharNumber")} {...register("aadharNumber")}
className="form-control form-control-sm" className="form-control "
id="aadharNumber" id="aadharNumber"
placeholder="AADHAR Number" placeholder="AADHAR Number"
maxLength={12} maxLength={12}
@ -569,7 +618,7 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
<input <input
type="text" type="text"
{...register("panNumber")} {...register("panNumber")}
className="form-control form-control-sm" className="form-control "
id="panNumber" id="panNumber"
placeholder="PAN Number" placeholder="PAN Number"
maxLength={10} maxLength={10}

View File

@ -37,6 +37,7 @@ import SelectEmployeeServerSide, {
} from "../common/Forms/SelectFieldServerSide"; } from "../common/Forms/SelectFieldServerSide";
import { useAllocationServiceProjectTeam } from "../../hooks/useServiceProject"; import { useAllocationServiceProjectTeam } from "../../hooks/useServiceProject";
import { AppFormController } from "../../hooks/appHooks/useAppForm"; import { AppFormController } from "../../hooks/appHooks/useAppForm";
import SelectField from "../common/Forms/SelectField";
const ManageExpense = ({ closeModal, expenseToEdit = null }) => { const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
const { const {
@ -182,15 +183,15 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
currencyId: data.currency.id || DEFAULT_CURRENCY, currencyId: data.currency.id || DEFAULT_CURRENCY,
billAttachments: data.documents billAttachments: data.documents
? data.documents.map((doc) => ({ ? data.documents.map((doc) => ({
fileName: doc.fileName, fileName: doc.fileName,
base64Data: null, base64Data: null,
contentType: doc.contentType, contentType: doc.contentType,
documentId: doc.documentId, documentId: doc.documentId,
fileSize: 0, fileSize: 0,
description: "", description: "",
preSignedUrl: doc.preSignedUrl, preSignedUrl: doc.preSignedUrl,
isActive: doc.isActive ?? true, isActive: doc.isActive ?? true,
})) }))
: [], : [],
}); });
} }
@ -236,7 +237,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
{expenseToEdit ? "Update Expense " : "Create New Expense"} {expenseToEdit ? "Update Expense " : "Create New Expense"}
</h5> </h5>
<form id="expenseForm" onSubmit={handleSubmit(onSubmit)}> <form id="expenseForm" onSubmit={handleSubmit(onSubmit)}>
<div className="row my-2 text-start"> <div className="row my-2 text-start">
<div className="col-12 col-md-6"> <div className="col-12 col-md-6">
<SelectProjectField <SelectProjectField
label="Project" label="Project"
@ -259,30 +260,32 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
<Label htmlFor="expenseCategoryId" className="form-label" required> <Label htmlFor="expenseCategoryId" className="form-label" required>
Expense Category Expense Category
</Label> </Label>
<select
className="form-select " <AppFormController
id="expenseCategoryId" name="expenseCategoryId"
{...register("expenseCategoryId")} control={control}
> rules={{ required: "Expense Category is required" }}
<option value="" disabled> render={({ field }) => (
Select Category <SelectField
</option> label="" // Label already shown above
{ExpenseLoading ? ( placeholder="Select Category"
<option disabled>Loading...</option> options={expenseCategories ?? []}
) : ( value={field.value || ""}
expenseCategories?.map((expense) => ( onChange={field.onChange}
<option key={expense.id} value={expense.id}> required
{expense.name} isLoading={ExpenseLoading}
</option> className="m-0 form-select-sm w-100"
)) />
)} )}
</select> />
{errors.expensesCategoryId && (
{errors.expenseCategoryId && (
<small className="danger-text"> <small className="danger-text">
{errors.expensesCategoryId.message} {errors.expenseCategoryId.message}
</small> </small>
)} )}
</div> </div>
</div> </div>
<div className="row my-2 text-start"> <div className="row my-2 text-start">
@ -290,40 +293,40 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
<Label htmlFor="paymentModeId" className="form-label" required> <Label htmlFor="paymentModeId" className="form-label" required>
Payment Mode Payment Mode
</Label> </Label>
<select
className="form-select " <AppFormController
id="paymentModeId" name="paymentModeId"
{...register("paymentModeId")} control={control}
> rules={{ required: "Payment Mode is required" }}
<option value="" disabled> render={({ field }) => (
Select Mode <SelectField
</option> label="" // Label is shown above
{PaymentModeLoading ? ( placeholder="Select Mode"
<option disabled>Loading...</option> options={PaymentModes ?? []}
) : ( value={field.value || ""}
PaymentModes?.map((payment) => ( onChange={field.onChange}
<option key={payment.id} value={payment.id}> required
{payment.name} isLoading={PaymentModeLoading}
</option> className="m-0 form-select-sm w-100"
)) />
)} )}
</select> />
{errors.paymentModeId && ( {errors.paymentModeId && (
<small className="danger-text"> <small className="danger-text">{errors.paymentModeId.message}</small>
{errors.paymentModeId.message}
</small>
)} )}
</div> </div>
<div className="col-12 col-md-6 text-start">
<div className="col-12 col-md-6 text-start">
<AppFormController <AppFormController
name="paidById" name="paidById"
control={control} control={control}
render={({ field }) => ( render={({ field }) => (
<SelectEmployeeServerSide <SelectEmployeeServerSide
label="Paid By" required label="Paid By" required
value={field.value} value={field.value}
onChange={field.onChange} onChange={field.onChange}
isFullObject={false} isFullObject={false}
/> />
)} )}
/> />
@ -437,32 +440,37 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
</div> </div>
</div> </div>
<div className="row mb-4"> <div className="row mb-4">
<div className="col-md-6 text-start "> <div className="col-md-6 text-start">
<Label htmlFor="currencyId" className="form-label" required> <Label htmlFor="currencyId" className="form-label" required>
Select Currency Select Currency
</Label> </Label>
<select
className="form-select " <AppFormController
id="currencyId" name="currencyId"
{...register("currencyId")} control={control}
> rules={{ required: "Currency is required" }}
<option value="" disabled> render={({ field }) => (
Select Currency <SelectField
</option> label="" // Label already shown above
{currencyLoading ? ( placeholder="Select Currency"
<option disabled>Loading...</option> options={currencies?.map((currency) => ({
) : ( id: currency.id,
currencies?.map((currency) => ( name: `${currency.currencyName} (${currency.symbol})`,
<option key={currency.id} value={currency.id}> })) ?? []}
{`${currency.currencyName} (${currency.symbol}) `} value={field.value || ""}
</option> onChange={field.onChange}
)) required
isLoading={currencyLoading}
className="m-0 form-select-sm w-100"
/>
)} )}
</select> />
{errors.currencyId && ( {errors.currencyId && (
<small className="danger-text">{errors.currencyId.message}</small> <small className="danger-text">{errors.currencyId.message}</small>
)} )}
</div> </div>
{expenseCategory?.noOfPersonsRequired && ( {expenseCategory?.noOfPersonsRequired && (
<div className="col-md-6 text-start"> <div className="col-md-6 text-start">
<Label className="form-label" required> <Label className="form-label" required>
@ -553,7 +561,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
(fileError?.fileSize?.message || (fileError?.fileSize?.message ||
fileError?.contentType?.message || fileError?.contentType?.message ||
fileError?.base64Data?.message, fileError?.base64Data?.message,
fileError?.documentId?.message) fileError?.documentId?.message)
} }
</div> </div>
))} ))}
@ -578,8 +586,8 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
{isPending || createPending {isPending || createPending
? "Please Wait..." ? "Please Wait..."
: expenseToEdit : expenseToEdit
? "Update" ? "Update"
: "Save as Draft"} : "Save as Draft"}
</button> </button>
</div> </div>
</form> </form>

View File

@ -30,6 +30,8 @@ import InputSuggestions from "../common/InputSuggestion";
import { useProfile } from "../../hooks/useProfile"; import { useProfile } from "../../hooks/useProfile";
import { blockUI } from "../../utils/blockUI"; import { blockUI } from "../../utils/blockUI";
import { SelectProjectField } from "../common/Forms/SelectFieldServerSide"; import { SelectProjectField } from "../common/Forms/SelectFieldServerSide";
import { AppFormController } from "../../hooks/appHooks/useAppForm";
import SelectField from "../common/Forms/SelectField";
function ManagePaymentRequest({ closeModal, requestToEdit = null }) { function ManagePaymentRequest({ closeModal, requestToEdit = null }) {
const { const {
@ -176,15 +178,15 @@ function ManagePaymentRequest({ closeModal, requestToEdit = null }) {
isAdvancePayment: data.isAdvancePayment || false, isAdvancePayment: data.isAdvancePayment || false,
billAttachments: data.attachments billAttachments: data.attachments
? data?.attachments?.map((doc) => ({ ? data?.attachments?.map((doc) => ({
fileName: doc.fileName, fileName: doc.fileName,
base64Data: null, base64Data: null,
contentType: doc.contentType, contentType: doc.contentType,
documentId: doc.id, documentId: doc.id,
fileSize: 0, fileSize: 0,
description: "", description: "",
preSignedUrl: doc.preSignedUrl, preSignedUrl: doc.preSignedUrl,
isActive: doc.isActive ?? true, isActive: doc.isActive ?? true,
})) }))
: [], : [],
}); });
} }
@ -266,9 +268,9 @@ function ManagePaymentRequest({ closeModal, requestToEdit = null }) {
shouldDirty: true, shouldDirty: true,
shouldValidate: true, shouldValidate: true,
}) })
} }
disabled={ disabled={
data?.recurringPayment?.isVariable && !isDraft && !isProcessed data?.recurringPayment?.isVariable && !isDraft && !isProcessed
} }
/> />
@ -281,33 +283,33 @@ function ManagePaymentRequest({ closeModal, requestToEdit = null }) {
<Label htmlFor="expenseCategoryId" className="form-label" required> <Label htmlFor="expenseCategoryId" className="form-label" required>
Expense Category Expense Category
</Label> </Label>
<select
className="form-select " <AppFormController
id="expenseCategoryId" name="expenseCategoryId"
{...register("expenseCategoryId")} control={control}
disabled={ rules={{ required: "Expense Category is required" }}
data?.recurringPayment?.isVariable && !isDraft && !isProcessed render={({ field }) => (
} <SelectField
> label="" // Label already above
<option value="" disabled> placeholder="Select Category"
Select Category options={expenseCategories ?? []}
</option> value={field.value || ""}
{ExpenseLoading ? ( onChange={field.onChange}
<option disabled>Loading...</option> required
) : ( isLoading={ExpenseLoading}
expenseCategories?.map((expense) => ( isDisabled={data?.recurringPayment?.isVariable && !isDraft && !isProcessed}
<option key={expense.id} value={expense.id}> className="m-0 form-select-sm w-100"
{expense.name} />
</option>
))
)} )}
</select> />
{errors.expenseCategoryId && ( {errors.expenseCategoryId && (
<small className="danger-text"> <small className="danger-text">
{errors.expenseCategoryId.message} {errors.expenseCategoryId.message}
</small> </small>
)} )}
</div> </div>
</div> </div>
{/* Title and Advance Payment */} {/* Title and Advance Payment */}
@ -458,30 +460,39 @@ function ManagePaymentRequest({ closeModal, requestToEdit = null }) {
<Label htmlFor="currencyId" className="form-label" required> <Label htmlFor="currencyId" className="form-label" required>
Currency Currency
</Label> </Label>
<select
id="currencyId"
className="form-select "
{...register("currencyId")}
disabled={
data?.recurringPayment?.isVariable && !isDraft && !isProcessed
}
>
<option value="">Select Currency</option>
{currencyLoading && <option>Loading...</option>} <AppFormController
name="currencyId"
control={control}
rules={{ required: "Currency is required" }}
render={({ field }) => (
<SelectField
label="" // Label already above
placeholder="Select Currency"
options={currencyData?.map((currency) => ({
id: currency.id,
name: `${currency.currencyName} (${currency.symbol})`,
})) ?? []}
value={field.value || ""}
onChange={field.onChange}
required
isLoading={currencyLoading}
isDisabled={data?.recurringPayment?.isVariable && !isDraft && !isProcessed}
noOptionsMessage={() =>
!currencyLoading && !currencyError && (!currencyData || currencyData.length === 0)
? "No currency found"
: null
}
className="m-0 form-select-sm w-100"
/>
)}
/>
{!currencyLoading &&
!currencyError &&
currencyData?.map((currency) => (
<option key={currency.id} value={currency.id}>
{`${currency.currencyName} (${currency.symbol})`}
</option>
))}
</select>
{errors.currencyId && ( {errors.currencyId && (
<small className="danger-text">{errors.currencyId.message}</small> <small className="danger-text">{errors.currencyId.message}</small>
)} )}
</div> </div>
</div> </div>
{/* Description */} {/* Description */}
@ -508,7 +519,7 @@ function ManagePaymentRequest({ closeModal, requestToEdit = null }) {
</div> </div>
{/* Upload Document */} {/* Upload Document */}
<div className="row my-2 text-start"> <div className="row my-4 text-start">
<div className="col-md-12"> <div className="col-md-12">
<Label className="form-label">Upload Bill </Label> <Label className="form-label">Upload Bill </Label>
@ -559,7 +570,7 @@ function ManagePaymentRequest({ closeModal, requestToEdit = null }) {
(fileError?.fileSize?.message || (fileError?.fileSize?.message ||
fileError?.contentType?.message || fileError?.contentType?.message ||
fileError?.base64Data?.message, fileError?.base64Data?.message,
fileError?.documentId?.message) fileError?.documentId?.message)
} }
</div> </div>
))} ))}
@ -582,8 +593,8 @@ function ManagePaymentRequest({ closeModal, requestToEdit = null }) {
{createPending || isPending {createPending || isPending
? "Please Wait..." ? "Please Wait..."
: requestToEdit : requestToEdit
? "Update" ? "Update"
: "Save as Draft"} : "Save as Draft"}
</button> </button>
</div> </div>
</form> </form>

View File

@ -18,6 +18,8 @@ import {
import eventBus from "../../services/eventBus"; import eventBus from "../../services/eventBus";
import { useCreateTask } from "../../hooks/useTasks"; import { useCreateTask } from "../../hooks/useTasks";
import Label from "../common/Label"; import Label from "../common/Label";
import { AppFormController } from "../../hooks/appHooks/useAppForm";
import SelectField from "../common/Forms/SelectField";
const TaskSchema = (maxPlanned) => { const TaskSchema = (maxPlanned) => {
return z.object({ return z.object({
@ -128,8 +130,8 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
const handleCheckboxChange = (event, user) => { const handleCheckboxChange = (event, user) => {
const updatedSelectedEmployees = event.target.checked const updatedSelectedEmployees = event.target.checked
? [...(watch("selectedEmployees") || []), user.id].filter( ? [...(watch("selectedEmployees") || []), user.id].filter(
(v, i, a) => a.indexOf(v) === i (v, i, a) => a.indexOf(v) === i
) )
: (watch("selectedEmployees") || []).filter((id) => id !== user.id); : (watch("selectedEmployees") || []).filter((id) => id !== user.id);
setValue("selectedEmployees", updatedSelectedEmployees); setValue("selectedEmployees", updatedSelectedEmployees);
@ -241,48 +243,57 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
<div className="row mb-1"> <div className="row mb-1">
<div className="col-12"> <div className="col-12">
<div className="row text-start"> <div className="row text-start">
<div className="col-12 col-md-8 d-flex flex-row gap-3 align-items-center"> <div className="col-12 col-md-8 d-flex flex-row gap-3 align-items-center mt-4">
<div> <div>
<select <AppFormController
className="form-select form-select-sm" name="organizationId"
value={selectedOrganization || ""} control={control}
onChange={(e) => rules={{ required: "Organization is required" }}
setSelectedOrganization(e.target.value) render={({ field }) => (
} <SelectField
> label="" // No label here, use external label if needed
{isServiceLoading ? ( options={organizationList ?? []}
<option>Loading...</option> placeholder="--Select Organization--"
) : ( required
<> labelKeyKey="name"
<option value="">--Select Organization--</option> valueKeyKey="id"
{organizationList?.map((org,index) => ( value={field.value || ""}
<option key={`${org.id}-${index}`} value={org.id}> onChange={field.onChange}
{org.name} isLoading={isServiceLoading}
</option> className="m-0 form-select-sm w-100"
))} />
</>
)} )}
</select> />
{errors.organizationId && (
<small className="danger-text">{errors.organizationId.message}</small>
)}
</div> </div>
<div> <div>
<select <AppFormController
className="form-select form-select-sm" name="serviceId"
value={selectedService || ""} control={control}
onChange={(e) => setSelectedService(e.target.value)} rules={{ required: "Service is required" }}
> render={({ field }) => (
{isOrgLoading ? ( <SelectField
<option>Loading...</option> label="" // No label as you have one above or elsewhere
) : ( options={serviceList ?? []}
<> placeholder="--Select Service--"
<option value="">--Select Service--</option> required
{serviceList?.map((service,index) => ( labelKeyKey="name"
<option key={`${service.id}-${index}`} value={service.id}> valueKeyKey="id"
{service.name} value={field.value || ""}
</option> onChange={field.onChange}
))} isLoading={isOrgLoading}
</> className="m-0 form-select-sm w-100"
/>
)} )}
</select> />
{errors.serviceId && (
<small className="danger-text">{errors.serviceId.message}</small>
)}
</div> </div>
</div> </div>
<div <div
@ -292,12 +303,11 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
{/* Dropdown */} {/* Dropdown */}
<div className="dropdown position-relative d-inline-block"> <div className="dropdown position-relative d-inline-block">
<a <a
className={`dropdown-toggle hide-arrow cursor-pointer ${ className={`dropdown-toggle hide-arrow cursor-pointer ${selectedRoles.includes("all") ||
selectedRoles.includes("all") ||
selectedRoles.length === 0 selectedRoles.length === 0
? "text-secondary" ? "text-secondary"
: "text-primary" : "text-primary"
}`} }`}
onClick={() => setOpen(!open)} onClick={() => setOpen(!open)}
> >
<i className="bx bx-slider-alt ms-2"></i> <i className="bx bx-slider-alt ms-2"></i>
@ -409,7 +419,7 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
<p className="text-center">Loading employees...</p> <p className="text-center">Loading employees...</p>
</div> </div>
) : filteredEmployees?.length > 0 ? ( ) : filteredEmployees?.length > 0 ? (
filteredEmployees.map((emp,index) => { filteredEmployees.map((emp, index) => {
const jobRole = jobRoleData?.find( const jobRole = jobRoleData?.find(
(role) => role?.id === emp?.jobRoleId (role) => role?.id === emp?.jobRoleId
); );
@ -479,14 +489,14 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
{watch("selectedEmployees")?.length > 0 && ( {watch("selectedEmployees")?.length > 0 && (
<div className="mt-1"> <div className="mt-1">
<div className="text-start px-2"> <div className="text-start px-2">
{watch("selectedEmployees")?.map((empId,ind) => { {watch("selectedEmployees")?.map((empId, ind) => {
const emp = employees?.data?.find( const emp = employees?.data?.find(
(emp) => emp.id === empId (emp) => emp.id === empId
); );
return ( return (
emp && ( emp && (
<span <span
key={`${empId}-${ind}`} key={`${empId}-${ind}`}
className="badge rounded-pill bg-label-primary d-inline-flex align-items-center me-1 mb-1" className="badge rounded-pill bg-label-primary d-inline-flex align-items-center me-1 mb-1"
> >
{emp.firstName} {emp.lastName} {emp.firstName} {emp.lastName}

View File

@ -3,11 +3,11 @@ import { useForm } from "react-hook-form";
import { z } from "zod"; import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { getCachedData } from "../../../slices/apiDataManager";
import showToast from "../../../services/toastService"; import showToast from "../../../services/toastService";
import { useManageProjectInfra } from "../../../hooks/useProjects"; import { useManageProjectInfra } from "../../../hooks/useProjects";
import useSelect from "../../common/useSelect";
import Label from "../../common/Label"; import Label from "../../common/Label";
import { AppFormController, AppFormProvider } from "../../../hooks/appHooks/useAppForm";
import SelectField from "../../common/Forms/SelectField";
const buildingSchema = z.object({ const buildingSchema = z.object({
Id: z.string().optional(), Id: z.string().optional(),
@ -22,14 +22,8 @@ const BuildingModel = ({ project, onClose, editingBuilding = null }) => {
const selectedProject = useSelector( const selectedProject = useSelector(
(store) => store.localVariables.projectId (store) => store.localVariables.projectId
); );
const {
register, const methods = useForm({
handleSubmit,
formState: { errors },
setValue,
watch,
reset,
} = useForm({
resolver: zodResolver(buildingSchema), resolver: zodResolver(buildingSchema),
defaultValues: { defaultValues: {
Id: "0", Id: "0",
@ -37,12 +31,23 @@ const BuildingModel = ({ project, onClose, editingBuilding = null }) => {
description: "", description: "",
}, },
}); });
const {
register,
handleSubmit,
control,
watch,
reset,
setValue,
formState: { errors },
} = methods;
const watchedId = watch("Id"); const watchedId = watch("Id");
const { mutate: ManageBuilding, isPending } = useManageProjectInfra({ const { mutate: ManageBuilding, isPending } = useManageProjectInfra({
onSuccessCallback: (data, variables) => { onSuccessCallback: () => {
showToast( showToast(
watchedId != "0" watchedId !== "0"
? "Building updated Successfully" ? "Building updated Successfully"
: "Building created Successfully", : "Building created Successfully",
"success" "success"
@ -79,7 +84,7 @@ const BuildingModel = ({ project, onClose, editingBuilding = null }) => {
description: editingBuilding.description, description: editingBuilding.description,
}); });
} }
}, [editingBuilding]); }, [editingBuilding, reset]);
const onSubmitHandler = (data) => { const onSubmitHandler = (data) => {
const payload = { const payload = {
@ -88,95 +93,109 @@ const BuildingModel = ({ project, onClose, editingBuilding = null }) => {
projectId: selectedProject, projectId: selectedProject,
}; };
let infraObject = [ const infraObject = [
{ {
building: payload, building: payload,
floor: null, floor: null,
workArea: null, workArea: null,
}, },
]; ];
ManageBuilding({ infraObject, projectId: selectedProject }); ManageBuilding({ infraObject, projectId: selectedProject });
}; };
return ( return (
<form onSubmit={handleSubmit(onSubmitHandler)} className="row g-2"> <AppFormProvider {...methods}>
<h5 className="text-center mb-2">Manage Buildings</h5> <form onSubmit={handleSubmit(onSubmitHandler)} className="row g-2">
<div className="col-12 text-start"> <h5 className="text-center mb-2">Manage Buildings</h5>
<label className="form-label">Select Building</label>
<select {/* Building Select */}
{...register("Id")} <div className="col-12 text-start">
className="select2 form-select form-select-sm" <Label htmlFor="Id" className="form-label">
> Select Building
<option value="0">Add New Building</option> </Label>
{sortedBuildings.length > 0 ? ( <AppFormController
sortedBuildings.map((b) => ( name="Id"
<option key={b.id} value={b.id}> control={control}
{b.buildingName} rules={{ required: "Building is required" }}
</option> render={({ field }) => (
)) <SelectField
) : ( label=""
<option disabled>No buildings found</option> placeholder="Select Building"
options={[
{ id: "0", name: "Add New Building" },
...(sortedBuildings?.map((b) => ({
id: String(b.id),
name: b.buildingName,
})) ?? []),
]}
value={field.value || ""}
onChange={field.onChange}
required
noOptionsMessage={() =>
!sortedBuildings || sortedBuildings.length === 0
? "No buildings found"
: null
}
className="m-0 form-select-sm w-100"
/>
)}
/>
{errors.Id && <span className="danger-text">{errors.Id.message}</span>}
</div>
{/* Name */}
<div className="col-12 text-start">
<Label className="form-label" required>
{watchedId !== "0" ? "Rename Building Name" : "New Building Name"}
</Label>
<input
{...register("name")}
type="text"
className="form-control "
/>
{errors.name && <span className="danger-text">{errors.name.message}</span>}
</div>
{/* Description */}
<div className="col-12 text-start">
<Label className="form-label" required>
Description
</Label>
<textarea
{...register("description")}
rows="5"
maxLength="160"
className="form-control "
/>
{errors.description && (
<span className="danger-text">{errors.description.message}</span>
)} )}
</select> </div>
{errors.Id && <span className="danger-text">{errors.Id.message}</span>}
</div>
{/* Name */} {/* Buttons */}
<div className="col-12 text-start"> <div className="col-12 text-end mt-6 my-2">
<Label className="form-label" required> <button
{watchedId !== "0" ? "Rename Building Name" : "New Building Name"} type="reset"
</Label> className="btn btn-sm btn-label-secondary me-3"
<input disabled={isPending}
{...register("name")} onClick={() => {
type="text" onClose();
className="form-control form-control-sm" reset();
/> }}
{errors.name && ( >
<span className="danger-text">{errors.name.message}</span> Cancel
)} </button>
</div> <button type="submit" className="btn btn-sm btn-primary" disabled={isPending}>
{isPending
{/* Description */} ? "Please wait..."
<div className="col-12 text-start"> : watchedId !== "0"
<Label className="form-label" required>Description</Label> ? "Edit Building"
<textarea : "Add Building"}
{...register("description")} </button>
rows="5" </div>
maxLength="160" </form>
className="form-control form-control-sm" </AppFormProvider>
/>
{errors.description && (
<span className="danger-text">{errors.description.message}</span>
)}
</div>
<div className="col-12 text-end mt-6 my-2">
<button
type="reset"
className="btn btn-sm btn-label-secondary me-3"
disabled={isPending}
onClick={() => {
onClose();
reset();
}}
>
Cancel
</button>
<button
type="submit"
className="btn btn-sm btn-primary"
disabled={isPending}
>
{isPending
? "Please wait..."
: watchedId !== "0"
? "Edit Building"
: "Add Building"}
</button>
</div>
</form>
); );
}; };

View File

@ -6,6 +6,8 @@ import showToast from "../../../services/toastService";
import { useManageProjectInfra } from "../../../hooks/useProjects"; import { useManageProjectInfra } from "../../../hooks/useProjects";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import Label from "../../common/Label"; import Label from "../../common/Label";
import { AppFormController, AppFormProvider } from "../../../hooks/appHooks/useAppForm";
import SelectField from "../../common/Forms/SelectField";
// Schema // Schema
const floorSchema = z.object({ const floorSchema = z.object({
@ -27,17 +29,12 @@ const FloorModel = ({ project, onClose, onSubmit }) => {
); );
const [selectedBuilding, setSelectedBuilding] = useState(null); const [selectedBuilding, setSelectedBuilding] = useState(null);
const { const methods = useForm({
register,
handleSubmit,
setValue,
reset,
watch,
formState: { errors },
} = useForm({
defaultValues, defaultValues,
resolver: zodResolver(floorSchema), resolver: zodResolver(floorSchema),
}); });
const { register, control, watch, handleSubmit, reset, setValue, formState: { errors } } = methods;
const watchId = watch("id"); const watchId = watch("id");
const watchBuildingId = watch("buildingId"); const watchBuildingId = watch("buildingId");
const { mutate: ManageFloor, isPending } = useManageProjectInfra({ const { mutate: ManageFloor, isPending } = useManageProjectInfra({
@ -48,7 +45,7 @@ const FloorModel = ({ project, onClose, onSubmit }) => {
: "Floor created Successfully", : "Floor created Successfully",
"success" "success"
); );
reset({ id: "0", floorName: ""}); reset({ id: "0", floorName: "" });
// onClose?.(); // onClose?.();
}, },
}); });
@ -98,94 +95,128 @@ const FloorModel = ({ project, onClose, onSubmit }) => {
}; };
return ( return (
<form className="row g-2" onSubmit={handleSubmit(onFormSubmit)}> <AppFormProvider {...methods}>
<div className="text-center mb-1"> <form className="row g-2" onSubmit={handleSubmit(onFormSubmit)}>
<h5 className="mb-1">Manage Floor</h5> <div className="text-center mb-1">
</div> <h5 className="mb-1">Manage Floor</h5>
<div className="col-12 text-start"> </div>
<Label className="form-label" required>Select Building</Label> <div className="col-12 text-start">
<select <Label className="form-label" required>
{...register("buildingId")} Select Building
className="form-select form-select-sm" </Label>
onChange={handleBuildingChange} <AppFormController
> name="buildingId"
<option value="0">Select Building</option> control={control}
{project?.length > 0 && rules={{ required: "Building is required" }}
project render={({ field }) => (
.filter((b) => b.buildingName) <SelectField
.sort((a, b) => a.buildingName.localeCompare(b.buildingName)) label=""
.map((b) => ( placeholder="Select Building"
<option key={b.id} value={b.id}> options={
{b.buildingName} project
</option> ?.filter((b) => b.buildingName)
))} .sort((a, b) => a.buildingName.localeCompare(b.buildingName))
</select> .map((b) => ({ id: String(b.id), name: b.buildingName })) ?? []
{errors.buildingId && ( }
<p className="danger-text">{errors.buildingId.message}</p> value={field.value || ""}
)} onChange={(value) => {
</div> field.onChange(value);
setValue("id", "0");
{watchBuildingId !== "0" && ( setValue("floorName", "");
<> }}
<div className="col-12 text-start"> required
<label className="form-label">Select Floor</label> noOptionsMessage={() => (!project || project.length === 0 ? "No buildings found" : null)}
<select className="m-0 form-select-sm w-100"
{...register("id")} />
className="form-select form-select-sm"
onChange={handleFloorChange}
>
<option value="0">Add New Floor</option>
{selectedBuilding?.floors?.length > 0 &&
selectedBuilding.floors
.filter((f) => f.floorName)
.sort((a, b) => a.floorName.localeCompare(b.floorName))
.map((f) => (
<option key={f.id} value={f.id}>
{f.floorName}
</option>
))}
</select>
</div>
<div className="col-12 text-start">
<Label className="form-label" required>
{watchId !== "0" ? "Edit Floor Name" : "New Floor Name"}
</Label>
<input
{...register("floorName")}
className="form-control form-control-sm"
placeholder="Floor Name"
/>
{errors.floorName && (
<p className="danger-text">{errors.floorName.message}</p>
)} )}
</div> />
</> {errors.buildingId && <p className="danger-text">{errors.buildingId.message}</p>}
)} </div>
<div className="col-12 text-end mt-6 my-2"> {watchBuildingId !== "0" && (
<button <>
type="button" <div className="col-12 text-start">
className="btn btn-sm btn-label-secondary me-3" <Label className="form-label">
disabled={isPending} Select Floor
onClick={onClose} </Label>
>
Cancel <AppFormController
</button> name="id"
<button control={control}
type="submit" rules={{ required: "Floor is required" }}
className="btn btn-sm btn-primary" render={({ field }) => (
disabled={isPending} <SelectField
> label="" // Label is already above
{isPending placeholder="Select Floor"
? "Please Wait" options={[
: watchId !== "0" { id: "0", name: "Add New Floor" },
? "Edit Floor" ...(selectedBuilding?.floors
: "Add Floor"} ?.filter((f) => f.floorName)
</button> .sort((a, b) => a.floorName.localeCompare(b.floorName))
.map((f) => ({ id: f.id, name: f.floorName })) ?? []),
</div> ]}
</form> value={field.value || ""}
onChange={(value) => {
field.onChange(value);
handleFloorChange?.(value);
}}
required
noOptionsMessage={() =>
!selectedBuilding?.floors || selectedBuilding.floors.length === 0
? "No floors found"
: null
}
className="m-0 form-select-sm w-100"
/>
)}
/>
{errors.id && (
<span className="danger-text">{errors.id.message}</span>
)}
</div>
<div className="col-12 text-start">
<Label className="form-label" required>
{watchId !== "0" ? "Edit Floor Name" : "New Floor Name"}
</Label>
<input
{...register("floorName")}
className="form-control"
placeholder="Floor Name"
/>
{errors.floorName && (
<p className="danger-text">{errors.floorName.message}</p>
)}
</div>
</>
)}
<div className="col-12 text-end mt-6 my-2">
<button
type="button"
className="btn btn-sm btn-label-secondary me-3"
disabled={isPending}
onClick={onClose}
>
Cancel
</button>
<button
type="submit"
className="btn btn-sm btn-primary"
disabled={isPending}
>
{isPending
? "Please Wait"
: watchId !== "0"
? "Edit Floor"
: "Add Floor"}
</button>
</div>
</form>
</AppFormProvider>
); );
}; };

View File

@ -17,6 +17,8 @@ import {
useOrganizationsList, useOrganizationsList,
} from "../../hooks/useOrganization"; } from "../../hooks/useOrganization";
import { localToUtc } from "../../utils/appUtils"; import { localToUtc } from "../../utils/appUtils";
import { AppFormController } from "../../hooks/appHooks/useAppForm";
import SelectField from "../common/Forms/SelectField";
const currentDate = new Date().toLocaleDateString("en-CA"); const currentDate = new Date().toLocaleDateString("en-CA");
const formatDate = (date) => { const formatDate = (date) => {
@ -42,8 +44,8 @@ const ManageProjectInfo = ({ project, onClose }) => {
1, 1,
true true
); );
const { mutate: UpdateProject, isPending } = useUpdateProject(() => {onClose?.()}); const { mutate: UpdateProject, isPending } = useUpdateProject(() => { onClose?.() });
const {mutate:CeateProject,isPending:isCreating} = useCreateProject(()=>{onClose?.()}) const { mutate: CeateProject, isPending: isCreating } = useCreateProject(() => { onClose?.() })
const { const {
register, register,
@ -74,7 +76,7 @@ const ManageProjectInfo = ({ project, onClose }) => {
pmcId: projects_Details?.pmc?.id || "", pmcId: projects_Details?.pmc?.id || "",
}); });
setAddressLength(projects_Details?.projectAddress?.length || 0); setAddressLength(projects_Details?.projectAddress?.length || 0);
}, [project, projects_Details, reset,data]); }, [project, projects_Details, reset, data]);
const onSubmitForm = (formData) => { const onSubmitForm = (formData) => {
if (project) { if (project) {
@ -85,8 +87,8 @@ const ManageProjectInfo = ({ project, onClose }) => {
id: project, id: project,
}; };
UpdateProject({ projectId: project, payload: payload }); UpdateProject({ projectId: project, payload: payload });
}else{ } else {
let payload = { let payload = {
...formData, ...formData,
startDate: localToUtc(formData.startDate), startDate: localToUtc(formData.startDate),
endDate: localToUtc(formData.endDate), endDate: localToUtc(formData.endDate),
@ -122,7 +124,7 @@ const ManageProjectInfo = ({ project, onClose }) => {
type="text" type="text"
id="name" id="name"
name="name" name="name"
className="form-control form-control-sm" className="form-control "
placeholder="Project Name" placeholder="Project Name"
{...register("name")} {...register("name")}
/> />
@ -143,7 +145,7 @@ const ManageProjectInfo = ({ project, onClose }) => {
type="text" type="text"
id="shortName" id="shortName"
name="shortName" name="shortName"
className="form-control form-control-sm" className="form-control "
placeholder="Short Name" placeholder="Short Name"
{...register("shortName")} {...register("shortName")}
/> />
@ -164,7 +166,7 @@ const ManageProjectInfo = ({ project, onClose }) => {
type="text" type="text"
id="contactPerson" id="contactPerson"
name="contactPerson" name="contactPerson"
className="form-control form-control-sm" className="form-control "
placeholder="Contact Person" placeholder="Contact Person"
maxLength={50} maxLength={50}
{...register("contactPerson")} {...register("contactPerson")}
@ -190,6 +192,7 @@ const ManageProjectInfo = ({ project, onClose }) => {
placeholder="DD-MM-YYYY" placeholder="DD-MM-YYYY"
maxDate={new Date()} // optional: restrict future dates maxDate={new Date()} // optional: restrict future dates
className="w-100" className="w-100"
size="md"
/> />
{errors.startDate && ( {errors.startDate && (
@ -213,6 +216,7 @@ const ManageProjectInfo = ({ project, onClose }) => {
placeholder="DD-MM-YYYY" placeholder="DD-MM-YYYY"
minDate={getValues("startDate")} // optional: restrict future dates minDate={getValues("startDate")} // optional: restrict future dates
className="w-100" className="w-100"
size="md"
/> />
{errors.endDate && ( {errors.endDate && (
@ -225,104 +229,109 @@ const ManageProjectInfo = ({ project, onClose }) => {
)} )}
</div> </div>
<div className="col-12 "> <div className="col-12">
<label className="form-label" htmlFor="modalEditUserStatus"> <Label htmlFor="modalEditUserStatus" className="form-label">
Status Status
</label> </Label>
<select
id="modalEditUserStatus" <AppFormController
name="modalEditUserStatus" name="projectStatusId"
className="select2 form-select form-select-sm" control={control}
aria-label="Default select example" rules={{ required: "Status is required" }}
{...register("projectStatusId", { render={({ field }) => (
required: "Status is required", <SelectField
valueAsNumber: false, label="" // Label is already above
})} placeholder="Select Status"
> options={PROJECT_STATUS?.map((status) => ({
{PROJECT_STATUS.map((status) => ( id: status.id,
<option key={status.id} value={status.id}> name: status.label,
{status.label} })) ?? []}
</option> value={field.value || ""}
))} onChange={field.onChange}
</select> required
className="m-0 form-select-sm w-100"
/>
)}
/>
{errors.projectStatusId && ( {errors.projectStatusId && (
<div <div className="danger-text text-start" style={{ fontSize: "12px" }}>
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.projectStatusId.message} {errors.projectStatusId.message}
</div> </div>
)} )}
</div> </div>
<div className="col-12 ">
<label className="form-label" htmlFor="modalEditUserStatus"> <div className="col-12 mt-n1">
<Label htmlFor="promoterId" className="form-label">
Promoter Promoter
</label> </Label>
<select
className="select2 form-select form-select-sm" <AppFormController
aria-label="Default select example" name="promoterId"
{...register("promoterId", { control={control}
required: "Promoter is required", rules={{ required: "Promoter is required" }}
valueAsNumber: false, render={({ field }) => (
})} <SelectField
> label="" // Label is already above
{isLoading ? ( placeholder="Select Promoter"
<option>Loading...</option> options={data?.data ?? []}
) : ( value={field.value || ""}
<> onChange={field.onChange}
<option value="">Select Promoter</option> required
{data?.data?.map((org) => ( isLoading={isLoading}
<option key={org.id} value={org.id}> className="m-0 form-select-sm w-100"
{org.name} noOptionsMessage={() =>
</option> !isLoading && (!data?.data || data.data.length === 0)
))} ? "No promoters found"
</> : null
}
/>
)} )}
</select> />
{errors.promoterId && ( {errors.promoterId && (
<div <div className="danger-text text-start" style={{ fontSize: "12px" }}>
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.promoterId.message} {errors.promoterId.message}
</div> </div>
)} )}
</div> </div>
<div className="col-12 ">
<label className="form-label" htmlFor="modalEditUserStatus"> <div className="col-12 mt-n1">
<Label htmlFor="pmcId" className="form-label">
PMC PMC
</label> </Label>
<select
className="select2 form-select form-select-sm" <AppFormController
aria-label="Default select example" name="pmcId"
{...register("pmcId", { control={control}
required: "Promoter is required", rules={{ required: "PMC is required" }}
valueAsNumber: false, render={({ field }) => (
})} <SelectField
> label="" // Label is already above
{isLoading ? ( placeholder="Select PMC"
<option>Loading...</option> options={data?.data ?? []}
) : ( value={field.value || ""}
<> onChange={field.onChange}
<option value="">Select PMC</option> required
{data?.data?.map((org) => ( isLoading={isLoading}
<option key={org.id} value={org.id}> className="m-0 form-select-sm w-100"
{org.name} noOptionsMessage={() =>
</option> !isLoading && (!data?.data || data.data.length === 0)
))} ? "No PMC found"
</> : null
}
/>
)} )}
</select> />
{errors.pmcId && ( {errors.pmcId && (
<div <div className="danger-text text-start" style={{ fontSize: "12px" }}>
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.pmcId.message} {errors.pmcId.message}
</div> </div>
)} )}
</div> </div>
<div className="d-flex justify-content-between text-secondary text-tiny text-wrap">
<div className="d-flex justify-content-between text-secondary text-tiny text-wrap mt-n1">
<span> <span>
<i className="bx bx-sm bx-info-circle"></i> Not found PMC and <i className="bx bx-sm bx-info-circle"></i> Not found PMC and
Pomoter, find through SPRID or create new Pomoter, find through SPRID or create new
@ -376,7 +385,7 @@ const ManageProjectInfo = ({ project, onClose }) => {
className="btn btn-primary btn-sm" className="btn btn-primary btn-sm"
disabled={isPending || isCreating || loading} disabled={isPending || isCreating || loading}
> >
{isPending||isCreating ? "Please Wait..." : project ? "Update" : "Submit"} {isPending || isCreating ? "Please Wait..." : project ? "Update" : "Submit"}
</button> </button>
</div> </div>
</form> </form>

View File

@ -28,6 +28,8 @@ import { useEmployeesName } from "../../hooks/useEmployees";
import PmsEmployeeInputTag from "../common/PmsEmployeeInputTag"; import PmsEmployeeInputTag from "../common/PmsEmployeeInputTag";
import HoverPopup from "../common/HoverPopup"; import HoverPopup from "../common/HoverPopup";
import { SelectProjectField } from "../common/Forms/SelectFieldServerSide"; import { SelectProjectField } from "../common/Forms/SelectFieldServerSide";
import { AppFormController } from "../../hooks/appHooks/useAppForm";
import SelectField from "../common/Forms/SelectField";
const ManageRecurringExpense = ({ closeModal, requestToEdit = null }) => { const ManageRecurringExpense = ({ closeModal, requestToEdit = null }) => {
const { const {
@ -200,30 +202,32 @@ const ManageRecurringExpense = ({ closeModal, requestToEdit = null }) => {
<Label htmlFor="expenseCategoryId" className="form-label" required> <Label htmlFor="expenseCategoryId" className="form-label" required>
Expense Category Expense Category
</Label> </Label>
<select
className="form-select" <AppFormController
id="expenseCategoryId" name="expenseCategoryId"
{...register("expenseCategoryId")} control={control}
> rules={{ required: "Expense Category is required" }}
<option value="" disabled> render={({ field }) => (
Select Category <SelectField
</option> label="" // Label is already above
{ExpenseLoading ? ( placeholder="Select Category"
<option disabled>Loading...</option> options={expenseCategories ?? []}
) : ( value={field.value || ""}
expenseCategories?.map((expense) => ( onChange={field.onChange}
<option key={expense.id} value={expense.id}> required
{expense.name} isLoading={ExpenseLoading}
</option> className="m-0 form-select-sm w-100"
)) />
)} )}
</select> />
{errors.expenseCategoryId && ( {errors.expenseCategoryId && (
<small className="danger-text"> <small className="danger-text">
{errors.expenseCategoryId.message} {errors.expenseCategoryId.message}
</small> </small>
)} )}
</div> </div>
</div> </div>
{/* Title and Is Variable */} {/* Title and Is Variable */}
@ -375,34 +379,45 @@ const ManageRecurringExpense = ({ closeModal, requestToEdit = null }) => {
<Label htmlFor="currencyId" className="form-label" required> <Label htmlFor="currencyId" className="form-label" required>
Currency Currency
</Label> </Label>
<select
id="currencyId"
className="form-select"
{...register("currencyId")}
>
<option value="">Select Currency</option>
{currencyLoading && <option>Loading...</option>} <AppFormController
name="currencyId"
control={control}
rules={{ required: "Currency is required" }}
render={({ field }) => (
<SelectField
label="" // Label already shown above
placeholder="Select Currency"
options={currencyData?.map((currency) => ({
id: currency.id,
name: `${currency.currencyName} (${currency.symbol})`,
})) ?? []}
value={field.value || ""}
onChange={field.onChange}
required
isLoading={currencyLoading}
noOptionsMessage={() =>
!currencyLoading && !currencyError && (!currencyData || currencyData.length === 0)
? "No currency found"
: null
}
className="m-0 form-select-sm w-100"
/>
)}
/>
{!currencyLoading &&
!currencyError &&
currencyData?.map((currency) => (
<option key={currency.id} value={currency.id}>
{`${currency.currencyName} (${currency.symbol})`}
</option>
))}
</select>
{errors.currencyId && ( {errors.currencyId && (
<small className="danger-text">{errors.currencyId.message}</small> <small className="danger-text">{errors.currencyId.message}</small>
)} )}
</div> </div>
</div> </div>
{/* Frequency To and Status Id */} {/* Frequency To and Status Id */}
<div className="row my-2 text-start mt-n2"> <div className="row my-2 text-start mt-n2">
<div className="col-md-6"> <div className="col-md-6">
<div className="d-flex justify-content-start align-items-center gap-2"> <div className="d-flex justify-content-start align-items-center gap-2">
<Label htmlFor="frequency" className="form-label mb-0" required> <Label htmlFor="frequency" className="form-label mb-1" required>
Frequency Frequency
</Label> </Label>
<HoverPopup <HoverPopup
@ -415,51 +430,69 @@ const ManageRecurringExpense = ({ closeModal, requestToEdit = null }) => {
</p> </p>
} }
> >
<i className="bx bx-info-circle bx-xs text-muted cursor-pointer"></i> <i className="bx bx-info-circle bx-xs text-muted cursor-pointer mb-1"></i>
</HoverPopup> </HoverPopup>
</div> </div>
<select <AppFormController
id="frequency" name="frequency"
className="form-select mt-1" control={control}
{...register("frequency", { valueAsNumber: true })} rules={{ required: "Frequency is required" }}
> render={({ field }) => (
<option value="">Select Frequency</option> <SelectField
{Object.entries(FREQUENCY_FOR_RECURRING).map(([key, label]) => ( label="" // Label shown above
<option key={key} value={key}> placeholder="Select Frequency"
{label} options={Object.entries(FREQUENCY_FOR_RECURRING).map(([key, label]) => ({
</option> id: key,
))} name: label,
</select> }))}
value={field.value || ""}
onChange={field.onChange}
required
className="m-0 mt-1"
/>
)}
/>
{errors.frequency && ( {errors.frequency && (
<small className="danger-text">{errors.frequency.message}</small> <small className="danger-text">{errors.frequency.message}</small>
)} )}
</div> </div>
<div className="col-md-6"> <div className="col-md-6">
<Label htmlFor="statusId" className="form-label" required> <Label htmlFor="statusId" className="form-label" required>
Status Status
</Label> </Label>
<select
id="statusId" <AppFormController
className="form-select" name="statusId"
{...register("statusId")} control={control}
> rules={{ required: "Status is required" }}
<option value="">Select Status</option> render={({ field }) => (
{statusLoading && <option>Loading...</option>} <SelectField
{!statusLoading && label="" // Label already shown above
!statusError && placeholder="Select Status"
statusData?.map((status) => ( options={statusData ?? []}
<option key={status.id} value={status.id}> value={field.value || ""}
{status.name} onChange={field.onChange}
</option> required
))} isLoading={statusLoading}
</select> noOptionsMessage={() =>
!statusLoading && !statusError && (!statusData || statusData.length === 0)
? "No status found"
: null
}
className="m-0 form-select-sm w-100"
/>
)}
/>
{errors.statusId && ( {errors.statusId && (
<small className="danger-text">{errors.statusId.message}</small> <small className="danger-text">{errors.statusId.message}</small>
)} )}
</div> </div>
</div> </div>
{/* Payment Buffer Days and End Date */} {/* Payment Buffer Days and End Date */}
@ -564,7 +597,7 @@ const ManageRecurringExpense = ({ closeModal, requestToEdit = null }) => {
</div> </div>
{/* Description */} {/* Description */}
<div className="row my-2 text-start"> <div className="row my-4 text-start">
<div className="col-md-12"> <div className="col-md-12">
<Label htmlFor="description" className="form-label" required> <Label htmlFor="description" className="form-label" required>
Description Description

View File

@ -23,6 +23,8 @@ import {
useServiceProject, useServiceProject,
useUpdateServiceProject, useUpdateServiceProject,
} from "../../hooks/useServiceProject"; } from "../../hooks/useServiceProject";
import { AppFormController } from "../../hooks/appHooks/useAppForm";
import SelectField from "../common/Forms/SelectField";
const ManageServiceProject = ({ serviceProjectId, onClose }) => { const ManageServiceProject = ({ serviceProjectId, onClose }) => {
const { const {
@ -117,51 +119,54 @@ const ManageServiceProject = ({ serviceProjectId, onClose }) => {
</div> </div>
<div className="row text-start"> <div className="row text-start">
<div className="col-12 mb-2"> <div className="col-12 mb-2">
<Label htmlFor="name" required> <Label htmlFor="clientId" required>
Client Client
</Label> </Label>
<div className="d-flex align-items-center gap-2">
<select <div className="d-flex align-items-center gap-2 w-100">
className="select2 form-select form-select-sm flex-grow-1" <div className="flex-grow-1" style={{ minWidth: "250px" }}>
aria-label="Default select example" <AppFormController
{...register("clientId", { name="clientId"
required: "Client is required", control={control}
valueAsNumber: false, rules={{ required: "Client is required" }}
})} render={({ field }) => (
> <SelectField
{isLoading ? ( label=""
<option>Loading...</option> options={organization?.data ?? []}
) : ( placeholder="Select Client"
<> required
<option value="">Select Client</option> labelKeyKey="name"
{organization?.data?.map((org) => ( valueKeyKey="id"
<option key={org.id} value={org.id}> value={field.value}
{org.name} onChange={field.onChange}
</option> isLoading={isLoading}
))} className="m-0 w-100"
</> />
)} )}
</select> />
</div>
<i <i
className="bx bx-plus-circle bx-xs cursor-pointer text-primary" className="bx bx-plus-circle bx-xs cursor-pointer text-primary mt-n3"
onClick={() => { onClick={() => {
onClose(); onClose();
openOrgModal({ startStep: 2 }); // Step 4 = ManagOrg openOrgModal({ startStep: 2 });
}} }}
/> />
</div> </div>
{errors?.clientId && ( {errors?.clientId && (
<span className="danger-text">{errors.clientId.message}</span> <small className="danger-text">{errors.clientId.message}</small>
)} )}
</div> </div>
<div className="col-12 mb-2"> <div className="col-12 mb-4">
<Label htmlFor="name" required> <Label htmlFor="name" required>
Project Name Project Name
</Label> </Label>
<input <input
type="text" type="text"
className="form-control form-control-sm" className="form-control "
{...register("name")} {...register("name")}
placeholder="Enter Project Name.." placeholder="Enter Project Name.."
/> />
@ -175,7 +180,7 @@ const ManageServiceProject = ({ serviceProjectId, onClose }) => {
</Label> </Label>
<input <input
type="text" type="text"
className="form-control form-control-sm" className="form-control "
{...register("shortName")} {...register("shortName")}
placeholder="Enter Project Short Name.." placeholder="Enter Project Short Name.."
/> />
@ -184,23 +189,37 @@ const ManageServiceProject = ({ serviceProjectId, onClose }) => {
)} )}
</div> </div>
<div className="col-12 col-md-6"> <div className="col-12 col-md-6">
<Label htmlFor="name" required> <Label htmlFor="statusId" required>
Select Status Select Status
</Label> </Label>
<select
className="form-select form-select-sm" <AppFormController
{...register("statusId")} name="statusId"
> control={control}
<option>Select Service</option> render={({ field }) => (
{PROJECT_STATUS?.map((status) => ( <SelectField
<option key={status.id} value={status.id}>{status.label}</option> label="" // label already above
))} options={PROJECT_STATUS?.map((status) => ({
</select> id: status.id,
name: status.label,
}))}
placeholder="Select Status"
required
labelKey="name"
valueKey="id"
value={field.value}
onChange={field.onChange}
className="form-select w-100"
/>
)}
/>
{errors?.statusId && ( {errors?.statusId && (
<span className="danger-text">{errors.statusId.message}</span> <span className="danger-text">{errors.statusId.message}</span>
)} )}
</div> </div>
<div className="col-12 mb-2">
<div className="col-12 mb-4">
<SelectMultiple <SelectMultiple
options={data?.data} options={data?.data}
isLoading={isLoading} isLoading={isLoading}
@ -214,15 +233,15 @@ const ManageServiceProject = ({ serviceProjectId, onClose }) => {
)} )}
</div> </div>
<div className="col-12 col-md-6 mb-2"> <div className="col-12 col-md-6 mb-4">
<Label htmlFor="name" required> <Label htmlFor="name" required>
Contact Person Contact Person
</Label> </Label>
<input <input
type="text" type="text"
className="form-control form-control-sm" className="form-control "
{...register("contactName")} {...register("contactName")}
placeholder="Enter Employee name.." placeholder="Enter Employee name.."
/> />
{errors?.contactName && ( {errors?.contactName && (
<span className="danger-text">{errors.contactName.message}</span> <span className="danger-text">{errors.contactName.message}</span>
@ -234,7 +253,7 @@ const ManageServiceProject = ({ serviceProjectId, onClose }) => {
</Label> </Label>
<input <input
type="text" type="text"
className="form-control form-control-sm" className="form-control "
{...register("contactEmail")} {...register("contactEmail")}
placeholder="Enter Employee Email.." placeholder="Enter Employee Email.."
/> />
@ -242,14 +261,14 @@ const ManageServiceProject = ({ serviceProjectId, onClose }) => {
<span className="danger-text">{errors.contactEmail.message}</span> <span className="danger-text">{errors.contactEmail.message}</span>
)} )}
</div> </div>
<div className="col-12 col-md-6 mb-2"> <div className="col-12 col-md-6 mb-4">
<Label htmlFor="name" required> <Label htmlFor="name" required>
Contact Number Contact Number
</Label> </Label>
<input <input
type="text" type="text"
maxLength={10} maxLength={10}
className="form-control form-control-sm" className="form-control "
{...register("contactPhone")} {...register("contactPhone")}
placeholder="Enter Employee Contact.." placeholder="Enter Employee Contact.."
onInput={(e) => { onInput={(e) => {
@ -268,6 +287,7 @@ const ManageServiceProject = ({ serviceProjectId, onClose }) => {
name="assignedDate" name="assignedDate"
className="w-100" className="w-100"
control={control} control={control}
size="md"
/> />
</div> </div>
<div className="col-12 col-md-12 mb-2"> <div className="col-12 col-md-12 mb-2">

View File

@ -12,6 +12,8 @@ import { formatUTCToLocalTime } from "../../utils/dateUtils";
import Avatar from "../common/Avatar"; import Avatar from "../common/Avatar";
import { PaymentHistorySkeleton } from "./CollectionSkeleton"; import { PaymentHistorySkeleton } from "./CollectionSkeleton";
import { usePaymentAjustmentHead } from "../../hooks/masterHook/useMaster"; import { usePaymentAjustmentHead } from "../../hooks/masterHook/useMaster";
import { AppFormController } from "../../hooks/appHooks/useAppForm";
import SelectField from "../common/Forms/SelectField";
const AddPayment = ({ onClose }) => { const AddPayment = ({ onClose }) => {
const { addPayment } = useCollectionContext(); const { addPayment } = useCollectionContext();
@ -60,7 +62,7 @@ const AddPayment = ({ onClose }) => {
<Label required>TransanctionId</Label> <Label required>TransanctionId</Label>
<input <input
type="text" type="text"
className="form-control form-control-sm" className="form-control "
{...register("transactionId")} {...register("transactionId")}
/> />
{errors.transactionId && ( {errors.transactionId && (
@ -78,10 +80,10 @@ const AddPayment = ({ onClose }) => {
minDate={ minDate={
data?.clientSubmitedDate data?.clientSubmitedDate
? new Date( ? new Date(
new Date(data?.clientSubmitedDate).setDate( new Date(data?.clientSubmitedDate).setDate(
new Date(data?.clientSubmitedDate).getDate() + 1 new Date(data?.clientSubmitedDate).getDate() + 1
)
) )
)
: null : null
} }
maxDate={new Date()} maxDate={new Date()}
@ -93,33 +95,30 @@ const AddPayment = ({ onClose }) => {
)} )}
</div> </div>
<div className="col-12 col-md-6 mb-2"> <div className="col-12 col-md-6 mb-2">
<Label <Label htmlFor="paymentAdjustmentHeadId" className="form-label" required>
htmlFor="paymentAdjustmentHeadId"
className="form-label"
required
>
Payment Adjustment Head Payment Adjustment Head
</Label> </Label>
<select
className="form-select form-select-sm " <AppFormController
{...register("paymentAdjustmentHeadId")} name="paymentAdjustmentHeadId"
> control={control}
{isPaymentTypeLoading ? ( rules={{ required: "Payment Adjustment Head is required" }}
<option>Loading..</option> render={({ field }) => (
) : ( <SelectField
<> label="" // Label is already above
<option value="">Select Payment Head</option> placeholder="Select Payment Head"
{paymentTypes?.data options={paymentTypes?.data
?.sort((a, b) => a.name.localeCompare(b.name)) ?.sort((a, b) => a.name.localeCompare(b.name)) ?? []}
?.map((type) => ( value={field.value || ""}
<option key={type.id} value={type.id}> onChange={field.onChange}
{type.name} required
</option> isLoading={isPaymentTypeLoading}
))} className="m-0 form-select-sm w-100"
</> />
)} )}
</select> />
{errors.paymentAdjustmentHeadId && ( {errors.paymentAdjustmentHeadId && (
<small className="danger-text"> <small className="danger-text">
{errors.paymentAdjustmentHeadId.message} {errors.paymentAdjustmentHeadId.message}
@ -127,6 +126,7 @@ const AddPayment = ({ onClose }) => {
)} )}
</div> </div>
<div className="col-12 col-md-6 mb-2"> <div className="col-12 col-md-6 mb-2">
<Label htmlFor="amount" className="form-label" required> <Label htmlFor="amount" className="form-label" required>
Amount Amount
@ -134,7 +134,7 @@ const AddPayment = ({ onClose }) => {
<input <input
type="number" type="number"
id="amount" id="amount"
className="form-control form-control-sm" className="form-control "
min="1" min="1"
step="0.01" step="0.01"
inputMode="decimal" inputMode="decimal"
@ -150,7 +150,7 @@ const AddPayment = ({ onClose }) => {
</Label> </Label>
<textarea <textarea
id="comment" id="comment"
className="form-control form-control-sm" className="form-control "
{...register("comment")} {...register("comment")}
/> />
{errors.comment && ( {errors.comment && (

View File

@ -19,6 +19,8 @@ import { formatDate } from "../../utils/dateUtils";
import { SelectProjectField } from "../common/Forms/SelectFieldServerSide"; import { SelectProjectField } from "../common/Forms/SelectFieldServerSide";
import { ITEMS_PER_PAGE } from "../../utils/constants"; import { ITEMS_PER_PAGE } from "../../utils/constants";
import { useOrganizationsList } from "../../hooks/useOrganization"; import { useOrganizationsList } from "../../hooks/useOrganization";
import { AppFormController } from "../../hooks/appHooks/useAppForm";
import SelectField from "../common/Forms/SelectField";
const ManageCollection = ({ collectionId, onClose }) => { const ManageCollection = ({ collectionId, onClose }) => {
const { data, isError, isLoading, error } = useCollection(collectionId); const { data, isError, isLoading, error } = useCollection(collectionId);
@ -189,37 +191,34 @@ const ManageCollection = ({ collectionId, onClose }) => {
</div> </div>
<div className="col-12 col-md-6 mb-2"> <div className="col-12 col-md-6 mb-2">
<Label htmlFor="name" required> <Label htmlFor="billedToId" required>
Bill To Bill To
</Label> </Label>
<div className="d-flex align-items-center gap-2">
<select <AppFormController
className="select2 form-select form-select flex-grow-1" name="billedToId"
aria-label="Default select example" control={control}
{...register("billedToId", { rules={{ required: "Client is required" }}
required: "Client is required", render={({ field }) => (
valueAsNumber: false, <SelectField
})} label="" // Label already shown above
> placeholder="Select Client"
{isLoading ? ( options={organization?.data ?? []}
<option>Loading...</option> value={field.value || ""}
) : ( onChange={field.onChange}
<> required
<option value="">Select Client</option> isLoading={isLoading}
{organization?.data?.map((org) => ( className="m-0 flex-grow-1"
<option key={org.id} value={org.id}> />
{org.name} )}
</option> />
))}
</> {errors?.billedToId && (
)}
</select>
</div>
{errors?.clientId && (
<span className="danger-text">{errors.billedToId.message}</span> <span className="danger-text">{errors.billedToId.message}</span>
)} )}
</div> </div>
<div className="col-12 col-md-6 mb-2"> <div className="col-12 col-md-6 mb-2">
<Label required>Title</Label> <Label required>Title</Label>
<input <input