Adding Radio Buttons at Manage Recurring Expense.

This commit is contained in:
Kartik Sharma 2025-11-05 16:19:44 +05:30
parent 92fa1c9a3d
commit ac9eaa1e67
4 changed files with 98 additions and 132 deletions

View File

@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import Label from '../common/Label'; import Label from '../common/Label';
import { useForm } from 'react-hook-form'; import { Controller, useForm } from 'react-hook-form';
import { useExpenseCategory, useRecurringStatus } from '../../hooks/masterHook/useMaster'; import { useExpenseCategory, useRecurringStatus } from '../../hooks/masterHook/useMaster';
import DatePicker from '../common/DatePicker'; import DatePicker from '../common/DatePicker';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
@ -11,24 +11,16 @@ import { useCreateRecurringExpense, usePayee, useUpdateRecurringExpense } from '
import InputSuggestions from '../common/InputSuggestion'; import InputSuggestions from '../common/InputSuggestion';
function ManageRecurringExpense({ closeModal, requestToEdit = null }) { function ManageRecurringExpense({ closeModal, requestToEdit = null }) {
const data = {} const data = {}
const { projectNames, loading: projectLoading, error, isError: isProjectError,
} = useProjectName();
//APIs
const { projectNames, loading: projectLoading, error, isError: isProjectError, } = useProjectName();
const { data: currencyData, isLoading: currencyLoading, isError: currencyError } = useCurrencies(); const { data: currencyData, isLoading: currencyLoading, isError: currencyError } = useCurrencies();
const { data: statusData, isLoading: statusLoading, isError: statusError } = useRecurringStatus(); const { data: statusData, isLoading: statusLoading, isError: statusError } = useRecurringStatus();
const { data: Payees, isLoading: isPayeeLoaing, isError: isPayeeError, error: payeeError } = usePayee() const { data: Payees, isLoading: isPayeeLoaing, isError: isPayeeError, error: payeeError } = usePayee()
const { ExpenseCategories, loading: ExpenseLoading, error: ExpenseError } = useExpenseCategory();
const {
ExpenseCategories,
loading: ExpenseLoading,
error: ExpenseError,
} = useExpenseCategory();
const schema = PaymentRecurringExpense(); const schema = PaymentRecurringExpense();
const { register, control, watch, handleSubmit, setValue, reset, formState: { errors }, } = useForm({ const { register, control, watch, handleSubmit, setValue, reset, formState: { errors }, } = useForm({
resolver: zodResolver(schema), resolver: zodResolver(schema),
defaultValues: defaultRecurringExpense, defaultValues: defaultRecurringExpense,
@ -82,7 +74,6 @@ function ManageRecurringExpense({ closeModal, requestToEdit = null }) {
}, [currencyData, requestToEdit, setValue]); }, [currencyData, requestToEdit, setValue]);
const onSubmit = (fromdata) => { const onSubmit = (fromdata) => {
let payload = { let payload = {
...fromdata, ...fromdata,
strikeDate: fromdata.strikeDate ? new Date(fromdata.strikeDate).toISOString() : null, strikeDate: fromdata.strikeDate ? new Date(fromdata.strikeDate).toISOString() : null,
@ -155,7 +146,6 @@ function ManageRecurringExpense({ closeModal, requestToEdit = null }) {
</small> </small>
)} )}
</div> </div>
</div> </div>
{/* Title and Is Variable */} {/* Title and Is Variable */}
@ -177,7 +167,7 @@ function ManageRecurringExpense({ closeModal, requestToEdit = null }) {
)} )}
</div> </div>
<div className="col-md-6"> {/* <div className="col-md-6">
<Label htmlFor="isVariable" className="form-label" required> <Label htmlFor="isVariable" className="form-label" required>
Is Variable Is Variable
</Label> </Label>
@ -194,10 +184,53 @@ function ManageRecurringExpense({ closeModal, requestToEdit = null }) {
{errors.isVariable && ( {errors.isVariable && (
<small className="danger-text">{errors.isVariable.message}</small> <small className="danger-text">{errors.isVariable.message}</small>
)} )}
</div> */}
<div className="col-md-6 mt-2">
<Label htmlFor="isVariable" className="form-label" required>
Payment Type
</Label>
<Controller
name="isVariable"
control={control}
defaultValue={defaultRecurringExpense.isVariable ?? false}
render={({ field }) => (
<div className="d-flex align-items-center gap-3">
<div className="form-check">
<input
type="radio"
id="isVariableTrue"
className="form-check-input"
checked={field.value === true}
onChange={() => field.onChange(true)}
/>
<Label htmlFor="isVariableTrue" className="form-check-label">
Is Variable
</Label>
</div>
<div className="form-check">
<input
type="radio"
id="isVariableFalse"
className="form-check-input"
checked={field.value === false}
onChange={() => field.onChange(false)}
/>
<Label htmlFor="isVariableFalse" className="form-check-label">
Fixed
</Label>
</div>
</div>
)}
/>
{errors.isVariable && (
<small className="danger-text">{errors.isVariable.message}</small>
)}
</div> </div>
</div> </div>
{/* Date and Amount */} {/* Date and Amount */}
<div className="row my-2 text-start"> <div className="row my-2 text-start">
<div className="col-md-6"> <div className="col-md-6">
@ -210,7 +243,6 @@ function ManageRecurringExpense({ closeModal, requestToEdit = null }) {
minDate={new Date()} minDate={new Date()}
className='w-100' className='w-100'
/> />
{errors.strikeDate && ( {errors.strikeDate && (
<small className="danger-text"> <small className="danger-text">
{errors.strikeDate.message} {errors.strikeDate.message}
@ -239,24 +271,6 @@ function ManageRecurringExpense({ closeModal, requestToEdit = null }) {
{/* Payee and Currency */} {/* Payee and Currency */}
<div className="row my-2 text-start"> <div className="row my-2 text-start">
{/* <div className="col-md-6">
<Label htmlFor="payee" className="form-label" required>
Payee (Supplier Name/Transporter Name/Other)
</Label>
<input
type="text"
id="payee"
className="form-control form-control-sm"
{...register("payee")}
/>
{errors.payee && (
<small className="danger-text">
{errors.payee.message}
</small>
)}
</div> */}
<div className="col-md-6"> <div className="col-md-6">
<Label htmlFor="payee" className="form-label" required> <Label htmlFor="payee" className="form-label" required>
Payee (Supplier Name/Transporter Name/Other) Payee (Supplier Name/Transporter Name/Other)
@ -268,11 +282,9 @@ function ManageRecurringExpense({ closeModal, requestToEdit = null }) {
setValue("payee", val, { shouldValidate: true }) setValue("payee", val, { shouldValidate: true })
} }
error={errors.payee?.message} error={errors.payee?.message}
/> />
</div> </div>
<div className="col-md-6"> <div className="col-md-6">
<Label htmlFor="currencyId" className="form-label" required> <Label htmlFor="currencyId" className="form-label" required>
Currency Currency
@ -298,10 +310,9 @@ function ManageRecurringExpense({ closeModal, requestToEdit = null }) {
<small className="danger-text">{errors.currencyId.message}</small> <small className="danger-text">{errors.currencyId.message}</small>
)} )}
</div> </div>
</div> </div>
{/* Notify To and Status Id */} {/* Frequency To and Status Id */}
<div className="row my-2 text-start"> <div className="row my-2 text-start">
<div className="col-md-6"> <div className="col-md-6">
<Label htmlFor="frequency" className="form-label" required> <Label htmlFor="frequency" className="form-label" required>
@ -344,7 +355,6 @@ function ManageRecurringExpense({ closeModal, requestToEdit = null }) {
<small className="danger-text">{errors.statusId.message}</small> <small className="danger-text">{errors.statusId.message}</small>
)} )}
</div> </div>
</div> </div>
{/* Payment Buffer Days and Number of Iteration */} {/* Payment Buffer Days and Number of Iteration */}
@ -383,14 +393,11 @@ function ManageRecurringExpense({ closeModal, requestToEdit = null }) {
</div> </div>
</div> </div>
{/* Frequency */} {/* Notify */}
<div className="row my-2 text-start"> <div className="row my-2 text-start">
<div className="col-md-6"> <div className="col-md-6">
<Label htmlFor="notifyTo" className="form-label" required> <Label htmlFor="notifyTo" className="form-label" required>
Notify (E-mail) Notify Employees
</Label> </Label>
<input <input
type="text" type="text"
@ -427,28 +434,6 @@ function ManageRecurringExpense({ closeModal, requestToEdit = null }) {
</div> </div>
</div> </div>
{/* <div className="d-flex justify-content-end gap-3">
<button
type="reset"
// disabled={createPending}
onClick={handleClose}
className="btn btn-label-secondary btn-sm mt-3"
>
Cancel
</button>
<button
type="submit"
className="btn btn-primary btn-sm mt-3"
// disabled={createPending}
>
{createPending
? "Please Wait..."
: requestToEdit
? "Update"
: "Submit"}
</button>
</div> */}
<div className="d-flex justify-content-end gap-3"> <div className="d-flex justify-content-end gap-3">
<button <button
type="reset" type="reset"
@ -464,8 +449,6 @@ function ManageRecurringExpense({ closeModal, requestToEdit = null }) {
Submit Submit
</button> </button>
</div> </div>
</form> </form>
</div> </div>
) )

View File

@ -29,12 +29,12 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
); );
const recurringExpenseColumns = [ const recurringExpenseColumns = [
{ // {
key: "recurringPaymentUId", // key: "recurringPaymentUId",
label: "Recurring Payment ID", // label: "Recurring Payment ID",
align: "text-start ps-2", // align: "text-start ps-2",
getValue: (e) => e?.recurringPaymentUId || "N/A", // getValue: (e) => e?.recurringPaymentUId || "N/A",
}, // },
{ {
key: "expenseCategory", key: "expenseCategory",
label: "Category", label: "Category",
@ -47,22 +47,13 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
align: "text-start", align: "text-start",
getValue: (e) => e?.title || "N/A", getValue: (e) => e?.title || "N/A",
}, },
{ // {
key: "strikeDate", // key: "strikeDate",
label: "Strike Date", // label: "Strike Date",
align: "text-center", // align: "text-center",
getValue: (e) => // getValue: (e) =>
e?.strikeDate ? formatUTCToLocalTime(e.strikeDate) : "N/A", // e?.strikeDate ? formatUTCToLocalTime(e.strikeDate) : "N/A",
}, // },
{
key: "amount",
label: "Amount",
align: "text-end",
getValue: (e) =>
e?.amount
? `${e?.currency?.symbol || ""}${e.amount.toLocaleString()}`
: "N/A",
},
{ {
key: "payee", key: "payee",
label: "Payee", label: "Payee",
@ -79,14 +70,25 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
: "N/A", : "N/A",
}, },
{ {
key: "latestPRGeneratedAt", key: "amount",
label: "Last Generation Date", label: "Amount",
align: "text-center", align: "text-end",
getValue: (e) => getValue: (e) =>
e?.latestPRGeneratedAt e?.amount
? formatUTCToLocalTime(e.latestPRGeneratedAt) ? `${e?.currency?.symbol || ""}${e.amount.toLocaleString()}`
: "N/A", : "N/A",
}, },
// {
// key: "latestPRGeneratedAt",
// label: "Last Generation Date",
// align: "text-center",
// getValue: (e) =>
// e?.latestPRGeneratedAt
// ? formatUTCToLocalTime(e.latestPRGeneratedAt)
// : "N/A",
// },
{ {
key: "createdAt", key: "createdAt",
label: "Next Generation Date", label: "Next Generation Date",
@ -124,10 +126,15 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
} }
const header = [ const header = [
"Template Name", "Recurring Payment ID",
"Category",
"Title",
"Strike Date",
"Amount",
"Payee",
"Frequency", "Frequency",
"Next Generation Date",
"Last Generation Date", "Last Generation Date",
"Next Generation",
"Status", "Status",
"Action", "Action",
]; ];
@ -197,7 +204,7 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
<tbody> <tbody>
{filteredData.length > 0 ? ( {filteredData.length > 0 ? (
filteredData.map((recurringExpense) => ( filteredData.map((recurringExpense) => (
<tr key={recurringExpense.id} className="align-middle" style={{ height: "40px" }}> <tr key={recurringExpense.id} className="align-middle" style={{ height: "50px" }}>
{recurringExpenseColumns.map((col) => ( {recurringExpenseColumns.map((col) => (
<td <td
key={col.key} key={col.key}
@ -209,7 +216,7 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
</td> </td>
))} ))}
<td className="sticky-action-column bg-white"> <td className="sticky-action-column bg-white">
<div className="d-flex justify-content-center gap-2"> <div className="d-flex justify-content-center gap-0">
<i <i
className="bx bx-show text-primary cursor-pointer" className="bx bx-show text-primary cursor-pointer"
// onClick={() => // onClick={() =>
@ -234,7 +241,7 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
onClick={() => onClick={() =>
setManageRequest({ setManageRequest({
IsOpen: true, IsOpen: true,
RequestId: recurringExpense.id, projectId: project.id,
}) })
} }
> >
@ -258,7 +265,6 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
</ul> </ul>
</div> </div>
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -64,7 +64,9 @@ export const PaymentRecurringExpense = (expenseTypes) => {
required_error: "Frequency is required", required_error: "Frequency is required",
invalid_type_error: "Frequency must be a number", invalid_type_error: "Frequency must be a number",
}) })
.min(1, { message: "Frequency must be greater than 0" }), .refine((val) => [0, 1, 2, 3, 4, 5].includes(val), {
message: "Invalid frequency selected",
}),
isVariable: z.boolean().optional(), isVariable: z.boolean().optional(),
}); });
@ -85,7 +87,7 @@ export const defaultRecurringExpense = {
expenseCategoryId: "", expenseCategoryId: "",
statusId: "", statusId: "",
frequency: 1, frequency: 1,
isVariable: false, isVariable: true,
}; };
@ -106,15 +108,5 @@ export const SearchRecurringExpenseSchema = z.object({
isVariable: z.string().optional(), isVariable: z.string().optional(),
}); });
// export const defaultPaymentRequestFilter = {
// projectIds: [],
// statusIds: [],
// createdByIds: [],
// currencyIds: [],
// expenseCategoryIds: [],
// payees: [],
// startDate: null,
// endDate: null,
// };

View File

@ -21,7 +21,6 @@ const RecurringExpensePage = () => {
RecurringId: null, RecurringId: null,
}); });
const [ViewRequest, setVieRequest] = useState({ view: false, requestId: null }) const [ViewRequest, setVieRequest] = useState({ view: false, requestId: null })
const { setOffcanvasContent, setShowTrigger } = useFab();
const [selectedStatuses, setSelectedStatuses] = useState( const [selectedStatuses, setSelectedStatuses] = useState(
PAYEE_RECURRING_EXPENSE.map((s) => s.id) PAYEE_RECURRING_EXPENSE.map((s) => s.id)
@ -34,20 +33,6 @@ const RecurringExpensePage = () => {
setVieRequest setVieRequest
}; };
useEffect(() => {
setShowTrigger(true);
setOffcanvasContent(
"Payment Request Filters",
// <PaymentRequestFilterPanel onApply={setFilters} />
);
return () => {
setShowTrigger(false);
setOffcanvasContent("", null);
};
}, []);
const handleStatusChange = (id) => { const handleStatusChange = (id) => {
setSelectedStatuses((prev) => setSelectedStatuses((prev) =>
prev.includes(id) prev.includes(id)
@ -73,7 +58,7 @@ const RecurringExpensePage = () => {
<div className="card-body py-2 px-1"> <div className="card-body py-2 px-1">
<div className="d-flex flex-wrap align-items-center justify-content-between"> <div className="d-flex flex-wrap align-items-center justify-content-between">
{/* Left side: Search + Filter */} {/* Left side: Search + Filter */}
<div className="d-flex align-items-center gap-2 flex-wrap"> <div className="d-flex align-items-center flex-wrap">
<input <input
type="search" type="search"
className="form-control form-control-sm w-auto" className="form-control form-control-sm w-auto"
@ -123,7 +108,7 @@ const RecurringExpensePage = () => {
> >
<i className="bx bx-plus-circle me-2"></i> <i className="bx bx-plus-circle me-2"></i>
<span className="d-none d-md-inline-block"> <span className="d-none d-md-inline-block">
Add Payment Request Add Recurring Expense
</span> </span>
</button> </button>
</div> </div>