@appcorp/fusion-storybook 0.4.21 → 0.4.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/base-modules/student-fee/context/shared.d.ts +5 -0
- package/base-modules/student-fee/context/shared.js +1 -0
- package/base-modules/student-fee/context/use-student-fee-module.d.ts +1 -0
- package/base-modules/student-fee/context/use-student-fee-module.js +21 -6
- package/base-modules/student-fee/misc-2.js +59 -31
- package/base-modules/student-fee/page.js +2 -1
- package/base-modules/student-fee/single-entry-helpers.d.ts +55 -0
- package/base-modules/student-fee/single-entry-helpers.js +33 -0
- package/package.json +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -66,6 +66,7 @@ export declare const STUDENT_FEE_ACTION_TYPES: {
|
|
|
66
66
|
appliedFilterFromDate: string;
|
|
67
67
|
appliedFilterToDate: string;
|
|
68
68
|
studentFeesSingleEntry: StudentFeeBE[];
|
|
69
|
+
selectedStudentFeeIds: string[];
|
|
69
70
|
}>, initialStudentFeeState: {
|
|
70
71
|
items: StudentFeeBE[];
|
|
71
72
|
count: number;
|
|
@@ -107,6 +108,7 @@ export declare const STUDENT_FEE_ACTION_TYPES: {
|
|
|
107
108
|
appliedFilterFromDate: string;
|
|
108
109
|
appliedFilterToDate: string;
|
|
109
110
|
studentFeesSingleEntry: StudentFeeBE[];
|
|
111
|
+
selectedStudentFeeIds: string[];
|
|
110
112
|
}, StudentFeeProvider: import("react").FC<{
|
|
111
113
|
children: React.ReactNode;
|
|
112
114
|
}>, studentFeeReducer: (state: {
|
|
@@ -150,6 +152,7 @@ export declare const STUDENT_FEE_ACTION_TYPES: {
|
|
|
150
152
|
appliedFilterFromDate: string;
|
|
151
153
|
appliedFilterToDate: string;
|
|
152
154
|
studentFeesSingleEntry: StudentFeeBE[];
|
|
155
|
+
selectedStudentFeeIds: string[];
|
|
153
156
|
}, action: any) => {
|
|
154
157
|
items: StudentFeeBE[];
|
|
155
158
|
count: number;
|
|
@@ -191,6 +194,7 @@ export declare const STUDENT_FEE_ACTION_TYPES: {
|
|
|
191
194
|
appliedFilterFromDate: string;
|
|
192
195
|
appliedFilterToDate: string;
|
|
193
196
|
studentFeesSingleEntry: StudentFeeBE[];
|
|
197
|
+
selectedStudentFeeIds: string[];
|
|
194
198
|
}, useStudentFeeContext: () => import("@react-pakistan/util-functions/factory/generic-module-factory").GenericModuleContextWithHandlers<{
|
|
195
199
|
items: StudentFeeBE[];
|
|
196
200
|
count: number;
|
|
@@ -232,4 +236,5 @@ export declare const STUDENT_FEE_ACTION_TYPES: {
|
|
|
232
236
|
appliedFilterFromDate: string;
|
|
233
237
|
appliedFilterToDate: string;
|
|
234
238
|
studentFeesSingleEntry: StudentFeeBE[];
|
|
239
|
+
selectedStudentFeeIds: string[];
|
|
235
240
|
}>;
|
|
@@ -72,6 +72,7 @@ const studentFeeConfig = {
|
|
|
72
72
|
appliedFilterFromDate: defaultFilterFromDate,
|
|
73
73
|
appliedFilterToDate: defaultFilterToDate,
|
|
74
74
|
studentFeesSingleEntry: [],
|
|
75
|
+
selectedStudentFeeIds: [],
|
|
75
76
|
},
|
|
76
77
|
};
|
|
77
78
|
// ============================================================================
|
|
@@ -78,6 +78,7 @@ export declare const useStudentFeeModule: () => {
|
|
|
78
78
|
appliedFilterFromDate: string;
|
|
79
79
|
appliedFilterToDate: string;
|
|
80
80
|
studentFeesSingleEntry: import("../../../types").StudentFeeBE[];
|
|
81
|
+
selectedStudentFeeIds: string[];
|
|
81
82
|
};
|
|
82
83
|
dispatch: React.Dispatch<any>;
|
|
83
84
|
};
|
|
@@ -9,6 +9,7 @@ import { generateThemeToast, TOAST_VARIANT, } from "@appcorp/shadcn/lib/toast-ut
|
|
|
9
9
|
import { PAYMENT_STATUS, DISCOUNT_TYPE, } from "../../../type";
|
|
10
10
|
import { STUDENT_FEE_API_ROUTES } from "../../../constants";
|
|
11
11
|
import { studentFeeFormValidation } from "../validate";
|
|
12
|
+
import { buildSingleEntryBody } from "../single-entry-helpers";
|
|
12
13
|
import { getCachedWorkspaceSync } from "../../workspace/cache";
|
|
13
14
|
import { getCachedFeeStructureById } from "../../fee-structure/cache";
|
|
14
15
|
import { getCachedDiscountCodesSync } from "../../discount-code/cache";
|
|
@@ -539,16 +540,25 @@ export const useStudentFeeModule = () => {
|
|
|
539
540
|
payload: { disabled: true },
|
|
540
541
|
});
|
|
541
542
|
try {
|
|
543
|
+
if (state.selectedStudentFeeIds.length === 0) {
|
|
544
|
+
showToast(t("messagesNoVoucherSelected"), TOAST_VARIANT.ERROR);
|
|
545
|
+
dispatch({
|
|
546
|
+
type: STUDENT_FEE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
|
|
547
|
+
payload: { disabled: false },
|
|
548
|
+
});
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
542
551
|
const res = await fetch(STUDENT_FEE_API_ROUTES.SINGLE_ENTRY, {
|
|
543
552
|
method: API_METHODS.POST,
|
|
544
553
|
headers: { "Content-Type": "application/json" },
|
|
545
|
-
body: JSON.stringify({
|
|
546
|
-
studentFees: state.studentFeesSingleEntry
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
554
|
+
body: JSON.stringify(buildSingleEntryBody({
|
|
555
|
+
studentFees: state.studentFeesSingleEntry,
|
|
556
|
+
selectedIds: state.selectedStudentFeeIds,
|
|
557
|
+
status: state.status,
|
|
558
|
+
paidAt: state.paidAt,
|
|
559
|
+
remarks: state.remarks,
|
|
550
560
|
schoolId,
|
|
551
|
-
}),
|
|
561
|
+
})),
|
|
552
562
|
});
|
|
553
563
|
if (!res.ok) {
|
|
554
564
|
const err = await res.json().catch(() => ({}));
|
|
@@ -567,6 +577,10 @@ export const useStudentFeeModule = () => {
|
|
|
567
577
|
type: STUDENT_FEE_ACTION_TYPES.SET_INPUT_FIELD,
|
|
568
578
|
payload: { key: "studentFeesSingleEntry", value: [] },
|
|
569
579
|
});
|
|
580
|
+
dispatch({
|
|
581
|
+
type: STUDENT_FEE_ACTION_TYPES.SET_INPUT_FIELD,
|
|
582
|
+
payload: { key: "selectedStudentFeeIds", value: [] },
|
|
583
|
+
});
|
|
570
584
|
dispatch({
|
|
571
585
|
type: STUDENT_FEE_ACTION_TYPES.SET_INPUT_FIELD,
|
|
572
586
|
payload: { key: "amountPaid", value: 0 },
|
|
@@ -611,6 +625,7 @@ export const useStudentFeeModule = () => {
|
|
|
611
625
|
state.remarks,
|
|
612
626
|
state.status,
|
|
613
627
|
state.keepPosting,
|
|
628
|
+
state.selectedStudentFeeIds,
|
|
614
629
|
]);
|
|
615
630
|
// ============================================================================
|
|
616
631
|
// 1.4.6 HEADER & ROW ACTIONS
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx,
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
/**
|
|
4
4
|
* StudentFee Filter
|
|
5
5
|
*
|
|
@@ -19,6 +19,7 @@ import { useFetch } from "@react-pakistan/util-functions/hooks/use-fetch";
|
|
|
19
19
|
import { STUDENT_FEE_API_ROUTES } from "../../constants";
|
|
20
20
|
import { API_METHODS } from "@react-pakistan/util-functions/constants/api-methods";
|
|
21
21
|
import { DISCOUNT_TYPE, PAYMENT_STATUS } from "../../types";
|
|
22
|
+
import { sortFeesOldestFirst, initialSelectedIds, computeSelectedTotals, formatVoucherMonth, } from "./single-entry-helpers";
|
|
22
23
|
import { PAYMENT_STATUS_OPTIONS } from "./constants";
|
|
23
24
|
import { Loader2 } from "lucide-react";
|
|
24
25
|
import { DATE_FORMATS, formatDate, } from "@react-pakistan/util-functions/general/format-date";
|
|
@@ -54,29 +55,40 @@ export const StudentFeeSingle = () => {
|
|
|
54
55
|
params: Object.assign({ computerNumber: debouncedComputerNumber, schoolId: (_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id }, (useAsOf ? { asOf: state.paidAt } : {})),
|
|
55
56
|
});
|
|
56
57
|
}, [debouncedComputerNumber, (_c = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _c === void 0 ? void 0 : _c.id, state.paidAt]);
|
|
57
|
-
const accumulatedAmount = useMemo(() => {
|
|
58
|
-
var _a;
|
|
59
|
-
if (!((_a = feeByComputerNumber === null || feeByComputerNumber === void 0 ? void 0 : feeByComputerNumber.studentFees) === null || _a === void 0 ? void 0 : _a.length))
|
|
60
|
-
return 0;
|
|
61
|
-
return feeByComputerNumber.studentFees.reduce((sum, f) => { var _a; return sum + ((_a = f.amount) !== null && _a !== void 0 ? _a : 0); }, 0);
|
|
62
|
-
}, [feeByComputerNumber]);
|
|
63
58
|
useEffect(() => {
|
|
64
59
|
var _a;
|
|
65
|
-
if (!((_a = feeByComputerNumber === null || feeByComputerNumber === void 0 ? void 0 : feeByComputerNumber.studentFees) === null || _a === void 0 ? void 0 : _a.length))
|
|
60
|
+
if (!((_a = feeByComputerNumber === null || feeByComputerNumber === void 0 ? void 0 : feeByComputerNumber.studentFees) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
61
|
+
dispatch({
|
|
62
|
+
type: STUDENT_FEE_ACTION_TYPES.SET_INPUT_FIELD,
|
|
63
|
+
payload: { key: "studentFeesSingleEntry", value: [] },
|
|
64
|
+
});
|
|
65
|
+
dispatch({
|
|
66
|
+
type: STUDENT_FEE_ACTION_TYPES.SET_INPUT_FIELD,
|
|
67
|
+
payload: { key: "selectedStudentFeeIds", value: [] },
|
|
68
|
+
});
|
|
69
|
+
dispatch({
|
|
70
|
+
type: STUDENT_FEE_ACTION_TYPES.SET_INPUT_FIELD,
|
|
71
|
+
payload: { key: "amountPaid", value: 0 },
|
|
72
|
+
});
|
|
66
73
|
return;
|
|
74
|
+
}
|
|
67
75
|
const fees = feeByComputerNumber.studentFees;
|
|
68
|
-
const
|
|
69
|
-
const
|
|
76
|
+
const sorted = sortFeesOldestFirst(fees);
|
|
77
|
+
const totals = computeSelectedTotals(sorted);
|
|
78
|
+
dispatch({
|
|
79
|
+
type: STUDENT_FEE_ACTION_TYPES.SET_INPUT_FIELD,
|
|
80
|
+
payload: { key: "studentFeesSingleEntry", value: sorted },
|
|
81
|
+
});
|
|
70
82
|
dispatch({
|
|
71
83
|
type: STUDENT_FEE_ACTION_TYPES.SET_INPUT_FIELD,
|
|
72
84
|
payload: {
|
|
73
|
-
key: "
|
|
74
|
-
value:
|
|
85
|
+
key: "selectedStudentFeeIds",
|
|
86
|
+
value: initialSelectedIds(sorted),
|
|
75
87
|
},
|
|
76
88
|
});
|
|
77
89
|
dispatch({
|
|
78
90
|
type: STUDENT_FEE_ACTION_TYPES.SET_INPUT_FIELD,
|
|
79
|
-
payload: { key: "amountPaid", value: total },
|
|
91
|
+
payload: { key: "amountPaid", value: totals.total },
|
|
80
92
|
});
|
|
81
93
|
dispatch({
|
|
82
94
|
type: STUDENT_FEE_ACTION_TYPES.SET_INPUT_FIELD,
|
|
@@ -90,7 +102,7 @@ export const StudentFeeSingle = () => {
|
|
|
90
102
|
type: STUDENT_FEE_ACTION_TYPES.SET_INPUT_FIELD,
|
|
91
103
|
payload: { key: "status", value: PAYMENT_STATUS.PAID },
|
|
92
104
|
});
|
|
93
|
-
}, [feeByComputerNumber, dispatch,
|
|
105
|
+
}, [feeByComputerNumber, dispatch, state.paidAt]);
|
|
94
106
|
const isStale = state.computerNumber !== debouncedComputerNumber;
|
|
95
107
|
const hasData = !!feeByComputerNumber &&
|
|
96
108
|
!!debouncedComputerNumber &&
|
|
@@ -100,13 +112,16 @@ export const StudentFeeSingle = () => {
|
|
|
100
112
|
? feeByComputerNumber
|
|
101
113
|
: null;
|
|
102
114
|
const profile = (_d = response === null || response === void 0 ? void 0 : response.studentProfile) !== null && _d !== void 0 ? _d : null;
|
|
103
|
-
const
|
|
115
|
+
const sortedFees = sortFeesOldestFirst(((_e = response === null || response === void 0 ? void 0 : response.studentFees) !== null && _e !== void 0 ? _e : []));
|
|
116
|
+
const fee = (_f = sortedFees[0]) !== null && _f !== void 0 ? _f : null;
|
|
104
117
|
useEffect(() => {
|
|
118
|
+
if (!fee)
|
|
119
|
+
return;
|
|
105
120
|
dispatch({
|
|
106
121
|
type: STUDENT_FEE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
|
|
107
|
-
payload: { disabled:
|
|
122
|
+
payload: { disabled: state.selectedStudentFeeIds.length === 0 },
|
|
108
123
|
});
|
|
109
|
-
}, [fee, dispatch]);
|
|
124
|
+
}, [fee, state.selectedStudentFeeIds.length, dispatch]);
|
|
110
125
|
const paymentStatusLabelMap = {
|
|
111
126
|
PENDING: t("formOptionPending"),
|
|
112
127
|
PARTIAL: t("formOptionPartial"),
|
|
@@ -127,17 +142,28 @@ export const StudentFeeSingle = () => {
|
|
|
127
142
|
// const d = new Date(rawDate);
|
|
128
143
|
// return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
129
144
|
// };
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
145
|
+
const selectedFees = useMemo(() => state.studentFeesSingleEntry.filter((f) => state.selectedStudentFeeIds.includes(f.id)), [state.studentFeesSingleEntry, state.selectedStudentFeeIds]);
|
|
146
|
+
const selectedTotals = useMemo(() => computeSelectedTotals(selectedFees), [selectedFees]);
|
|
147
|
+
const handleToggleVoucher = (feeId, checked) => {
|
|
148
|
+
const next = checked
|
|
149
|
+
? [...state.selectedStudentFeeIds, feeId]
|
|
150
|
+
: state.selectedStudentFeeIds.filter((id) => id !== feeId);
|
|
151
|
+
dispatch({
|
|
152
|
+
type: STUDENT_FEE_ACTION_TYPES.SET_INPUT_FIELD,
|
|
153
|
+
payload: { key: "selectedStudentFeeIds", value: next },
|
|
154
|
+
});
|
|
155
|
+
const nextSelected = state.studentFeesSingleEntry.filter((f) => next.includes(f.id));
|
|
156
|
+
dispatch({
|
|
157
|
+
type: STUDENT_FEE_ACTION_TYPES.SET_INPUT_FIELD,
|
|
158
|
+
payload: {
|
|
159
|
+
key: "amountPaid",
|
|
160
|
+
value: computeSelectedTotals(nextSelected).total,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
dispatch({
|
|
164
|
+
type: STUDENT_FEE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
|
|
165
|
+
payload: { disabled: next.length === 0 },
|
|
166
|
+
});
|
|
141
167
|
};
|
|
142
168
|
const { enhancedComboboxElement: statusCombo } = useEnhancedCombobox({
|
|
143
169
|
emptyText: t("formNoStatusEmpty"),
|
|
@@ -162,7 +188,9 @@ export const StudentFeeSingle = () => {
|
|
|
162
188
|
},
|
|
163
189
|
value: state.status,
|
|
164
190
|
});
|
|
165
|
-
return (_jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedCheckbox, { checked: state.keepPosting, id: "keepPosting", info: t("formKeepPostingInfo"), label: t("formKeepPostingLabel"), onCheckedChange: (checked) => handleChange("keepPosting", !!checked) }), _jsx(EnhancedInput, { id: "computerNumber", info: t("formComputerNumberInfo"), label: t("formComputerNumberLabel"), onChange: (e) => handleChange("computerNumber", e.target.value), placeholder: t("formComputerNumberPlaceholder"), type: "number", required: true, value: state.computerNumber }), fetchingByCN && debouncedComputerNumber ? (_jsx("div", { className: "flex items-center justify-center py-4", children: _jsx(Loader2, { className: "text-muted-foreground h-5 w-5 animate-spin" }) })) : null, fee && profile ? (_jsxs(
|
|
166
|
-
|
|
167
|
-
|
|
191
|
+
return (_jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedCheckbox, { checked: state.keepPosting, id: "keepPosting", info: t("formKeepPostingInfo"), label: t("formKeepPostingLabel"), onCheckedChange: (checked) => handleChange("keepPosting", !!checked) }), _jsx(EnhancedInput, { id: "computerNumber", info: t("formComputerNumberInfo"), label: t("formComputerNumberLabel"), onChange: (e) => handleChange("computerNumber", e.target.value), placeholder: t("formComputerNumberPlaceholder"), type: "number", required: true, value: state.computerNumber }), fetchingByCN && debouncedComputerNumber ? (_jsx("div", { className: "flex items-center justify-center py-4", children: _jsx(Loader2, { className: "text-muted-foreground h-5 w-5 animate-spin" }) })) : null, fee && profile ? (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium", children: t("formVouchersLabel") }), _jsx("p", { className: "text-muted-foreground text-xs", children: t("formVouchersInfo") })] }), _jsxs("div", { className: "text-muted-foreground grid grid-cols-[1.5fr_2fr_auto_auto_auto] items-center gap-2 px-3 text-xs", children: [_jsx("span", { children: t("formVouchersMonthLabel") }), _jsx("span", { children: t("formVouchersFeeStructureLabel") }), _jsx("span", { children: t("formVouchersAmountLabel") }), _jsx("span", { children: t("formVouchersFineLabel") }), _jsx("span", { children: t("formVouchersDueDateLabel") })] }), sortedFees.map((f) => {
|
|
192
|
+
var _a, _b, _c;
|
|
193
|
+
const checked = state.selectedStudentFeeIds.includes(f.id);
|
|
194
|
+
return (_jsxs("div", { className: `grid grid-cols-[1.5fr_2fr_auto_auto_auto] items-center gap-2 rounded-md border px-3 py-2 transition-opacity ${checked ? "opacity-100" : "opacity-50"}`, children: [_jsx(EnhancedCheckbox, { checked: checked, id: `voucher-${f.id}`, label: formatVoucherMonth(f.dueDate), onCheckedChange: (v) => handleToggleVoucher(f.id, !!v) }), _jsx("span", { className: "text-sm", children: ((_a = f.feeStructure) === null || _a === void 0 ? void 0 : _a.name) || f.feeStructureId || "" }), _jsx("span", { className: "text-sm", children: (_b = f.amount) !== null && _b !== void 0 ? _b : 0 }), _jsx("span", { className: "text-sm", children: (_c = f.fine) !== null && _c !== void 0 ? _c : 0 }), _jsx("span", { className: "text-sm", children: formatDate(f.dueDate, DATE_FORMATS.YYYY_MM_DD) })] }, f.id));
|
|
195
|
+
})] })) : null, fee && profile ? (_jsxs(_Fragment, { children: [_jsx(EnhancedInput, { readOnly: true, id: "studentProfileId-readonly", label: t("formStudentLabel"), value: studentName }), _jsx(EnhancedInput, { readOnly: true, id: "feeStructureId-readonly", label: t("formFeeStructureLabel"), value: feeStructureName }), _jsx(EnhancedInput, { readOnly: true, id: "discountCodeId-readonly", label: t("formDiscountCodeLabel"), value: discountCodeLabel }), _jsx(EnhancedInput, { readOnly: true, id: "discountAmount-readonly", label: t("formDiscountAmountLabel"), value: (_h = fee === null || fee === void 0 ? void 0 : fee.discountAmount) !== null && _h !== void 0 ? _h : 0, type: "number", step: "0.01" }), _jsx(EnhancedInput, { readOnly: true, id: "accumulatedFine-readonly", info: t("formAccumulatedFineInfo"), label: t("formAccumulatedFineLabel"), value: selectedTotals.fine, type: "number", step: "0.01" }), _jsx(EnhancedInput, { readOnly: true, id: "amount-readonly", info: t("formTotalAmountInfo"), label: t("formTotalAmountLabel"), value: selectedTotals.total, type: "number", step: "0.01" }), _jsx(EnhancedInput, { readOnly: true, id: "amountPaid", info: t("formAmountPaidInfo"), label: t("formAmountPaidLabel"), value: selectedTotals.total, type: "number", step: "0.01" }), _jsx(EnhancedInput, { readOnly: true, id: "amountDue-readonly", info: t("formAmountDueInfo"), label: t("formAmountDueLabel"), value: 0, type: "number", step: "0.01" }), _jsx(EnhancedInput, { readOnly: true, id: "dueDate-readonly", label: t("formDueDateLabel"), value: formatDate(fee === null || fee === void 0 ? void 0 : fee.dueDate, DATE_FORMATS.YYYY_MM_DD), type: "date" }), _jsx(EnhancedInput, { id: "paidAt", info: t("formPaidAtInfo"), label: t("formPaidAtLabel"), onChange: (e) => handleChange("paidAt", e.target.value), onClick: (e) => { var _a, _b; return (_b = (_a = e.currentTarget).showPicker) === null || _b === void 0 ? void 0 : _b.call(_a); }, type: "date", value: state.paidAt || "" }), statusCombo, _jsx(EnhancedTextarea, { id: "remarks", label: t("formRemarksLabel"), value: state.remarks || "", onChange: (e) => handleChange("remarks", e.target.value) })] })) : null] }));
|
|
168
196
|
};
|
|
@@ -147,7 +147,7 @@ const StudentFeePageInner = (props) => {
|
|
|
147
147
|
drawerFormTitle: props.drawerFormTitle,
|
|
148
148
|
drawerMisc1Description: props.drawerMisc1Description,
|
|
149
149
|
drawerMisc1Title: props.drawerMisc1Title,
|
|
150
|
-
drawerMisc2Description: props.
|
|
150
|
+
drawerMisc2Description: props.drawerMisc2Description,
|
|
151
151
|
drawerMisc2Title: props.drawerMisc2Title,
|
|
152
152
|
drawerMoreActionsDescription: props.drawerMoreActionsDescription,
|
|
153
153
|
drawerMoreActionsTitle: props.drawerMoreActionsTitle,
|
|
@@ -177,6 +177,7 @@ const StudentFeePageInner = (props) => {
|
|
|
177
177
|
props.drawerFormTitle,
|
|
178
178
|
props.drawerMisc1Description,
|
|
179
179
|
props.drawerMisc1Title,
|
|
180
|
+
props.drawerMisc2Description,
|
|
180
181
|
props.drawerMisc2Title,
|
|
181
182
|
props.drawerMoreActionsDescription,
|
|
182
183
|
props.drawerMoreActionsTitle,
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { PAYMENT_STATUS, StudentFeeBE } from "../../types";
|
|
2
|
+
export interface SelectedTotals {
|
|
3
|
+
amount: number;
|
|
4
|
+
fine: number;
|
|
5
|
+
total: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function sortFeesOldestFirst(fees: StudentFeeBE[]): StudentFeeBE[];
|
|
8
|
+
export declare function initialSelectedIds(fees: StudentFeeBE[]): string[];
|
|
9
|
+
export declare function computeSelectedTotals(fees: StudentFeeBE[]): SelectedTotals;
|
|
10
|
+
export declare function formatVoucherMonth(dueDate: Date | string): string;
|
|
11
|
+
export interface BuildSingleEntryBodyInput {
|
|
12
|
+
studentFees: StudentFeeBE[];
|
|
13
|
+
selectedIds: string[];
|
|
14
|
+
status: PAYMENT_STATUS;
|
|
15
|
+
paidAt: string | null;
|
|
16
|
+
remarks: string | null;
|
|
17
|
+
schoolId: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function buildSingleEntryBody(input: BuildSingleEntryBodyInput): {
|
|
20
|
+
studentFees: {
|
|
21
|
+
status: PAYMENT_STATUS;
|
|
22
|
+
amount: number;
|
|
23
|
+
amountDue: number;
|
|
24
|
+
amountPaid: number;
|
|
25
|
+
createdAt: string;
|
|
26
|
+
discountAmount: number | null;
|
|
27
|
+
discountCodeId: string | null;
|
|
28
|
+
dueDate: Date | string;
|
|
29
|
+
enabled: boolean;
|
|
30
|
+
familyId: string;
|
|
31
|
+
feeStructureId: string;
|
|
32
|
+
fine: number | null;
|
|
33
|
+
id: string;
|
|
34
|
+
lastRemindedAt: Date | string | null;
|
|
35
|
+
nextFollowUpAt: Date | string | null;
|
|
36
|
+
reliabilityScore: number | null;
|
|
37
|
+
paidAt: Date | string | null;
|
|
38
|
+
remarks: string | null;
|
|
39
|
+
riskLevel: import("../../types").FEE_RISK_LEVEL | null;
|
|
40
|
+
schoolId: string;
|
|
41
|
+
studentProfileId: string;
|
|
42
|
+
updatedAt: string;
|
|
43
|
+
agentActivities?: import("../../types").FeeAgentActivityBE[];
|
|
44
|
+
conversations?: import("../../types").FeeConversationBE[];
|
|
45
|
+
discountCode?: import("../../types").DiscountCodeBE;
|
|
46
|
+
family?: import("../../types").FamilyBE;
|
|
47
|
+
feeStructure?: import("../../types").FeeStructureBE;
|
|
48
|
+
school?: import("../../types").SchoolBE;
|
|
49
|
+
studentProfile?: import("../../types").StudentProfileBE;
|
|
50
|
+
}[];
|
|
51
|
+
amountPaid: number;
|
|
52
|
+
paidAt: string | null;
|
|
53
|
+
remarks: string;
|
|
54
|
+
schoolId: string;
|
|
55
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function sortFeesOldestFirst(fees) {
|
|
2
|
+
return [...fees].sort((a, b) => new Date(a.dueDate).getTime() - new Date(b.dueDate).getTime());
|
|
3
|
+
}
|
|
4
|
+
export function initialSelectedIds(fees) {
|
|
5
|
+
return fees.map((fee) => fee.id);
|
|
6
|
+
}
|
|
7
|
+
export function computeSelectedTotals(fees) {
|
|
8
|
+
return fees.reduce((acc, fee) => {
|
|
9
|
+
var _a, _b, _c, _d;
|
|
10
|
+
return ({
|
|
11
|
+
amount: acc.amount + ((_a = fee.amount) !== null && _a !== void 0 ? _a : 0),
|
|
12
|
+
fine: acc.fine + ((_b = fee.fine) !== null && _b !== void 0 ? _b : 0),
|
|
13
|
+
total: acc.total + ((_c = fee.amount) !== null && _c !== void 0 ? _c : 0) + ((_d = fee.fine) !== null && _d !== void 0 ? _d : 0),
|
|
14
|
+
});
|
|
15
|
+
}, { amount: 0, fine: 0, total: 0 });
|
|
16
|
+
}
|
|
17
|
+
export function formatVoucherMonth(dueDate) {
|
|
18
|
+
const date = new Date(dueDate);
|
|
19
|
+
if (isNaN(date.getTime()))
|
|
20
|
+
return "";
|
|
21
|
+
return date.toLocaleDateString("en-US", { month: "long", year: "numeric" });
|
|
22
|
+
}
|
|
23
|
+
export function buildSingleEntryBody(input) {
|
|
24
|
+
const selected = input.studentFees.filter((fee) => input.selectedIds.includes(fee.id));
|
|
25
|
+
const totals = computeSelectedTotals(selected);
|
|
26
|
+
return {
|
|
27
|
+
studentFees: selected.map((fee) => (Object.assign(Object.assign({}, fee), { status: input.status }))),
|
|
28
|
+
amountPaid: totals.total,
|
|
29
|
+
paidAt: input.paidAt || null,
|
|
30
|
+
remarks: input.remarks || "",
|
|
31
|
+
schoolId: input.schoolId,
|
|
32
|
+
};
|
|
33
|
+
}
|