@appcorp/fusion-storybook 0.1.95 → 0.1.96
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/course/context.js +1 -0
- package/base-modules/expense/form.js +4 -1
- package/base-modules/fee-structure/form.js +80 -7
- package/base-modules/section/context.js +3 -0
- package/base-modules/student-fee/form.js +169 -21
- package/package.json +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -123,6 +123,7 @@ export const useCourseModule = () => {
|
|
|
123
123
|
});
|
|
124
124
|
}, [theme]);
|
|
125
125
|
const resetFormAndCloseDrawer = useCallback(() => {
|
|
126
|
+
dispatch({ type: COURSE_ACTION_TYPES.RESET_FORM });
|
|
126
127
|
dispatch({
|
|
127
128
|
type: COURSE_ACTION_TYPES.SET_ERRORS,
|
|
128
129
|
payload: { errors: {} },
|
|
@@ -10,6 +10,7 @@ import { EnhancedCombobox } from "@appcorp/shadcn/components/enhanced-combobox";
|
|
|
10
10
|
import { EnhancedInput } from "@appcorp/shadcn/components/enhanced-input";
|
|
11
11
|
import { EnhancedTextarea } from "@appcorp/shadcn/components/enhanced-textarea";
|
|
12
12
|
import { EnhancedDropzone } from "@appcorp/shadcn/components/enhanced-dropzone";
|
|
13
|
+
import { useState } from "react";
|
|
13
14
|
import { useExpenseModule } from "./context";
|
|
14
15
|
import { EXPENSE_CATEGORY_OPTIONS, EXPENSE_STATUS_OPTIONS, PAYMENT_METHOD_OPTIONS, } from "./constants";
|
|
15
16
|
import { getCachedWorkspaceSync } from "../workspace/cache";
|
|
@@ -21,7 +22,9 @@ export const ExpenseForm = () => {
|
|
|
21
22
|
const workspace = getCachedWorkspaceSync();
|
|
22
23
|
const currency = (_b = (_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.currency) !== null && _b !== void 0 ? _b : "";
|
|
23
24
|
const t = useTranslations("expense");
|
|
24
|
-
|
|
25
|
+
const [categorySearchQuery, setCategorySearchQuery] = useState("");
|
|
26
|
+
const filteredCategoryOptions = EXPENSE_CATEGORY_OPTIONS.filter((opt) => opt.label.toLowerCase().includes(categorySearchQuery.toLowerCase()));
|
|
27
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(EnhancedInput, { error: errors.title, id: "title", info: t("formTitleInfo"), label: t("formTitleLabel"), onChange: (e) => handleChange("title", e.target.value), placeholder: t("formTitlePlaceholder"), required: true, type: "text", value: title || "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoCategoryEmpty"), error: errors.category, id: "category", info: t("formCategoryInfo"), label: t("formCategoryLabel"), onSearchChange: setCategorySearchQuery, onValueChange: (value) => handleChange("category", value), options: filteredCategoryOptions, placeholder: t("formCategoryPlaceholder"), required: true, searchPlaceholder: t("formSearchCategoriesPlaceholder"), value: category || "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoStatusEmpty"), error: errors.status, id: "status", info: t("formStatusInfo"), label: t("formOptionStatus"), onValueChange: (value) => handleChange("status", value), options: EXPENSE_STATUS_OPTIONS, placeholder: t("formStatusPlaceholder"), required: true, searchPlaceholder: t("formSearchStatusPlaceholder"), value: status || "" }), _jsx(EnhancedInput, { error: errors.amount, id: "amount", info: `${t("formAmountInfo")} ${currency}`, label: t("formAmountLabel"), min: "0", onChange: (e) => handleChange("amount", Number(e.target.value)), placeholder: "0.00", required: true, step: "0.01", type: "number", value: amount }), _jsx(EnhancedInput, { error: errors.expenseDate, id: "expenseDate", info: t("formExpenseDateInfo"), label: t("formExpenseDateLabel"), onChange: (e) => handleChange("expenseDate", e.target.value), required: true, type: "date", value: expenseDate || "" }), _jsx(EnhancedTextarea, { error: errors.description, id: "description", label: t("formDescriptionLabel"), onChange: (e) => handleChange("description", e.target.value), placeholder: t("formDescriptionPlaceholder"), rows: 3, value: description || "" }), _jsx(EnhancedInput, { error: errors.vendorName, id: "vendorName", info: t("formVendorNameInfo"), label: t("formVendorNameLabel"), onChange: (e) => handleChange("vendorName", e.target.value), placeholder: t("formVendorNamePlaceholder"), type: "text", value: vendorName || "" }), _jsx(EnhancedInput, { error: errors.vendorContact, id: "vendorContact", info: t("formVendorContactInfo"), label: t("formVendorContactLabel"), onChange: (e) => handleChange("vendorContact", e.target.value), placeholder: t("formVendorContactPlaceholder"), type: "text", value: vendorContact || "" }), _jsx(EnhancedInput, { error: errors.invoiceNumber, id: "invoiceNumber", info: t("formInvoiceNumberInfo"), label: t("formInvoiceNumberLabel"), onChange: (e) => handleChange("invoiceNumber", e.target.value), placeholder: t("formInvoiceNumberPlaceholder"), type: "text", value: invoiceNumber || "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoPaymentMethodEmpty"), error: errors.paymentMethod, id: "paymentMethod", info: t("formPaymentMethodInfo"), label: t("formPaymentMethodLabel"), onValueChange: (value) => handleChange("paymentMethod", value), options: PAYMENT_METHOD_OPTIONS, placeholder: t("formPaymentMethodPlaceholder"), searchPlaceholder: t("formSearchPaymentMethodsPlaceholder"), value: paymentMethod || "" }), _jsx(EnhancedInput, { error: errors.paymentDate, id: "paymentDate", info: t("formPaymentDateInfo"), label: t("formPaymentDateLabel"), onChange: (e) => handleChange("paymentDate", e.target.value), type: "date", value: paymentDate || "" }), _jsx(EnhancedInput, { error: errors.receiptNumber, id: "receiptNumber", info: t("formReceiptNumberInfo"), label: t("formReceiptNumberLabel"), onChange: (e) => handleChange("receiptNumber", e.target.value), placeholder: t("formReceiptNumberPlaceholder"), type: "text", value: receiptNumber || "" }), _jsx(EnhancedInput, { error: errors.transactionId, id: "transactionId", info: t("formTransactionIdInfo"), label: t("formTransactionIdLabel"), onChange: (e) => handleChange("transactionId", e.target.value), placeholder: t("formTransactionIdPlaceholder"), type: "text", value: transactionId || "" }), _jsx(EnhancedTextarea, { error: errors.remarks, id: "remarks", label: t("formRemarksLabel"), onChange: (e) => handleChange("remarks", e.target.value), placeholder: t("formRemarksPlaceholder"), rows: 3, value: remarks || "" }), _jsx(EnhancedDropzone, { accept: ["image/*", "application/pdf"], error: errors.attachments, id: "attachments", info: t("formAttachmentsInfo"), label: t("formAttachmentsLabel"), maxFiles: 10, onChange: (files) => {
|
|
25
28
|
const urls = files.map((f) => URL.createObjectURL(f));
|
|
26
29
|
handleChange("attachments", urls);
|
|
27
30
|
}, onRemoveRemote: (url) => {
|
|
@@ -9,27 +9,100 @@ import { EnhancedCheckbox } from "@appcorp/shadcn/components/enhanced-checkbox";
|
|
|
9
9
|
import { EnhancedCombobox } from "@appcorp/shadcn/components/enhanced-combobox";
|
|
10
10
|
import { EnhancedInput } from "@appcorp/shadcn/components/enhanced-input";
|
|
11
11
|
import { EnhancedTextarea } from "@appcorp/shadcn/components/enhanced-textarea";
|
|
12
|
+
import { useDebounce } from "@react-pakistan/util-functions/hooks/use-debounce";
|
|
13
|
+
import { useEffect, useMemo, useState } from "react";
|
|
12
14
|
import { useFeeStructureModule } from "./context";
|
|
13
15
|
import { FEE_TYPE_OPTIONS, FREQUENCY_OPTIONS } from "./constants";
|
|
14
|
-
import { getCachedClassesSync } from "../class/cache";
|
|
16
|
+
import { getCachedClasses, getCachedClassesSync } from "../class/cache";
|
|
15
17
|
import { getCachedWorkspaceSync } from "../workspace/cache";
|
|
16
18
|
import { useTranslations } from "next-intl";
|
|
17
19
|
export const FeeStructureForm = () => {
|
|
18
|
-
var _a, _b;
|
|
20
|
+
var _a, _b, _c;
|
|
19
21
|
const { state, handleChange } = useFeeStructureModule();
|
|
20
22
|
const { amount, classId, description, enabled, errors, feeType, frequency, name, } = state;
|
|
21
23
|
const t = useTranslations("feeStructure");
|
|
22
24
|
const workspace = getCachedWorkspaceSync();
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
const schoolId = ((_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id) || "";
|
|
26
|
+
const cachedClasses = getCachedClassesSync();
|
|
27
|
+
const [classSearchQuery, setClassSearchQuery] = useState("");
|
|
28
|
+
const [remoteClassOptions, setRemoteClassOptions] = useState(() => cachedClasses.items.map((cls) => ({
|
|
29
|
+
label: `${cls.name} (${cls.code})`,
|
|
30
|
+
value: cls.id,
|
|
31
|
+
})));
|
|
32
|
+
const [classOptionsLoading, setClassOptionsLoading] = useState(false);
|
|
33
|
+
const debouncedClassSearchQuery = useDebounce(classSearchQuery, 300);
|
|
34
|
+
const trimmedClassSearchQuery = debouncedClassSearchQuery.trim();
|
|
35
|
+
const cachedClassOptions = useMemo(() => cachedClasses.items.map((cls) => ({
|
|
36
|
+
label: `${cls.name} (${cls.code})`,
|
|
37
|
+
value: cls.id,
|
|
38
|
+
})), [cachedClasses.items]);
|
|
39
|
+
const selectedClassOption = useMemo(() => {
|
|
40
|
+
const selectedClass = cachedClasses.items.find((item) => item.id === classId);
|
|
41
|
+
return selectedClass
|
|
42
|
+
? [
|
|
43
|
+
{
|
|
44
|
+
label: `${selectedClass.name} (${selectedClass.code})`,
|
|
45
|
+
value: selectedClass.id,
|
|
46
|
+
},
|
|
47
|
+
]
|
|
48
|
+
: [];
|
|
49
|
+
}, [cachedClasses.items, classId]);
|
|
50
|
+
const displayedClassOptions = useMemo(() => {
|
|
51
|
+
const sourceOptions = trimmedClassSearchQuery
|
|
52
|
+
? remoteClassOptions
|
|
53
|
+
: cachedClassOptions;
|
|
54
|
+
const mergedOptions = [...selectedClassOption, ...sourceOptions];
|
|
55
|
+
const uniqueOptions = new Map(mergedOptions.map((option) => [option.value, option]));
|
|
56
|
+
return [...uniqueOptions.values()];
|
|
57
|
+
}, [
|
|
58
|
+
cachedClassOptions,
|
|
59
|
+
remoteClassOptions,
|
|
60
|
+
selectedClassOption,
|
|
61
|
+
trimmedClassSearchQuery,
|
|
62
|
+
]);
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (!trimmedClassSearchQuery || !schoolId)
|
|
65
|
+
return;
|
|
66
|
+
let isActive = true;
|
|
67
|
+
const fetchClasses = async () => {
|
|
68
|
+
setClassOptionsLoading(true);
|
|
69
|
+
try {
|
|
70
|
+
const { items } = await getCachedClasses({
|
|
71
|
+
params: {
|
|
72
|
+
schoolId,
|
|
73
|
+
searchQuery: trimmedClassSearchQuery,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
if (!isActive)
|
|
77
|
+
return;
|
|
78
|
+
setRemoteClassOptions((items || []).map((cls) => ({
|
|
79
|
+
label: `${cls.name} (${cls.code})`,
|
|
80
|
+
value: cls.id,
|
|
81
|
+
})));
|
|
82
|
+
}
|
|
83
|
+
catch (_a) {
|
|
84
|
+
if (!isActive)
|
|
85
|
+
return;
|
|
86
|
+
setRemoteClassOptions([]);
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
if (isActive) {
|
|
90
|
+
setClassOptionsLoading(false);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
void fetchClasses();
|
|
95
|
+
return () => {
|
|
96
|
+
isActive = false;
|
|
97
|
+
};
|
|
98
|
+
}, [schoolId, trimmedClassSearchQuery]);
|
|
26
99
|
return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedInput, { error: errors.name, id: "name", info: t("formNameInfo"), label: t("formNameLabel"), onChange: (e) => handleChange("name", e.target.value), placeholder: t("formNamePlaceholder"), required: true, value: name || "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoFeeTypeEmpty"), error: errors.feeType, id: "feeType", info: t("formFeeTypeInfo"), label: t("formOptionFeeType"), onValueChange: (value) => handleChange("feeType", value), options: FEE_TYPE_OPTIONS.map((opt) => ({
|
|
27
100
|
label: t(`feeType${opt.value.charAt(0).toUpperCase() + opt.value.slice(1).toLowerCase()}`),
|
|
28
101
|
value: opt.value,
|
|
29
102
|
})), placeholder: t("formFeeTypePlaceholder"), required: true, searchPlaceholder: t("formSearchFeeTypePlaceholder"), value: feeType || "" }), _jsx(EnhancedInput, { error: errors.amount, id: "amount", info: t(`amountInfo`, {
|
|
30
|
-
currency: (
|
|
103
|
+
currency: (_c = (_b = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _b === void 0 ? void 0 : _b.currency) !== null && _c !== void 0 ? _c : "",
|
|
31
104
|
}), label: t("formAmountLabel"), min: "0", onChange: (e) => handleChange("amount", Number(e.target.value)), placeholder: t("formAmountPlaceholder"), required: true, step: "0.01", type: "number", value: amount }), _jsx(EnhancedCombobox, { emptyText: t("formNoFrequencyEmpty"), id: "frequency", info: t("formFrequencyInfo"), label: t("formOptionFrequency"), onValueChange: (value) => handleChange("frequency", value), options: FREQUENCY_OPTIONS.map((opt) => ({
|
|
32
105
|
label: t(`frequency${opt.value.replace(/-/g, "")}`),
|
|
33
106
|
value: opt.value,
|
|
34
|
-
})), placeholder: t("formFrequencyPlaceholder"), searchPlaceholder: t("formSearchFrequencyPlaceholder"), value: frequency || "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoClassEmpty"), id: "classId", info: t("formClassInfo"), label: t("formClassLabel"), onValueChange: (value) => handleChange("classId", value || null), options:
|
|
107
|
+
})), placeholder: t("formFrequencyPlaceholder"), searchPlaceholder: t("formSearchFrequencyPlaceholder"), value: frequency || "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoClassEmpty"), id: "classId", info: t("formClassInfo"), label: t("formClassLabel"), loading: classOptionsLoading && Boolean(trimmedClassSearchQuery), onSearchChange: setClassSearchQuery, onValueChange: (value) => handleChange("classId", value || null), options: displayedClassOptions, placeholder: t("formClassPlaceholder"), searchPlaceholder: t("formSearchClassPlaceholder"), value: classId || "" }), _jsx(EnhancedTextarea, { error: errors.description, id: "description", info: t("formDescriptionInfo"), label: t("formDescriptionLabel"), onChange: (e) => handleChange("description", e.target.value), placeholder: t("formDescriptionPlaceholder"), rows: 3, value: description || "" })] }), _jsx(EnhancedCheckbox, { defaultChecked: enabled, label: t("formActiveFeeStructureLabel"), onCheckedChange: (checked) => handleChange("enabled", checked) })] }));
|
|
35
108
|
};
|
|
@@ -120,6 +120,9 @@ export const useSectionModule = () => {
|
|
|
120
120
|
});
|
|
121
121
|
}, [theme]);
|
|
122
122
|
const resetFormAndCloseDrawer = useCallback(() => {
|
|
123
|
+
dispatch({
|
|
124
|
+
type: SECTION_ACTION_TYPES.RESET_FORM,
|
|
125
|
+
});
|
|
123
126
|
dispatch({
|
|
124
127
|
type: SECTION_ACTION_TYPES.SET_ERRORS,
|
|
125
128
|
payload: { errors: {} },
|
|
@@ -10,22 +10,187 @@ import { EnhancedInput } from "@appcorp/shadcn/components/enhanced-input";
|
|
|
10
10
|
import { EnhancedCheckbox } from "@appcorp/shadcn/components/enhanced-checkbox";
|
|
11
11
|
import { EnhancedTextarea } from "@appcorp/shadcn/components/enhanced-textarea";
|
|
12
12
|
import { EnhancedCombobox } from "@appcorp/shadcn/components/enhanced-combobox";
|
|
13
|
+
import { useDebounce } from "@react-pakistan/util-functions/hooks/use-debounce";
|
|
14
|
+
import { useEffect, useMemo, useState } from "react";
|
|
13
15
|
import { useStudentFeeModule } from "./context";
|
|
14
16
|
import { PAYMENT_STATUS_OPTIONS } from "./constants";
|
|
15
|
-
import { getCachedStudentProfilesSync } from "../student-profile/cache";
|
|
16
|
-
import { getCachedFeeStructuresSync } from "../fee-structure/cache";
|
|
17
|
+
import { getCachedStudentProfiles, getCachedStudentProfilesSync, } from "../student-profile/cache";
|
|
18
|
+
import { getCachedFeeStructures, getCachedFeeStructuresSync, } from "../fee-structure/cache";
|
|
17
19
|
import { getCachedDiscountCodesSync } from "../discount-code/cache";
|
|
18
20
|
import { getCachedWorkspaceSync } from "../workspace/cache";
|
|
19
21
|
import { DATE_FORMATS, formatDate } from "@react-pakistan/util-functions";
|
|
20
22
|
import { DISCOUNT_TYPE } from "../../type";
|
|
21
23
|
import { useTranslations } from "next-intl";
|
|
22
24
|
export const StudentFeeForm = () => {
|
|
23
|
-
var _a, _b;
|
|
25
|
+
var _a, _b, _c;
|
|
24
26
|
const { state, handleChange } = useStudentFeeModule();
|
|
25
27
|
const { amount, amountDue, amountPaid, discountAmount, discountCodeId, dueDate, enabled, errors, familyId, feeStructureId, remarks, status, studentProfileId, } = state;
|
|
26
28
|
const workspace = getCachedWorkspaceSync();
|
|
27
29
|
const currency = (_b = (_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.currency) !== null && _b !== void 0 ? _b : "";
|
|
30
|
+
const schoolId = ((_c = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _c === void 0 ? void 0 : _c.id) || "";
|
|
28
31
|
const t = useTranslations("studentFee");
|
|
32
|
+
const cachedStudentProfiles = getCachedStudentProfilesSync();
|
|
33
|
+
const [studentSearchQuery, setStudentSearchQuery] = useState("");
|
|
34
|
+
const [remoteStudentOptions, setRemoteStudentOptions] = useState(() => cachedStudentProfiles.items.map((s) => {
|
|
35
|
+
var _a, _b;
|
|
36
|
+
return ({
|
|
37
|
+
label: `${s.computerNumber} - ${(_a = s.familyMember) === null || _a === void 0 ? void 0 : _a.firstName} ${(_b = s.familyMember) === null || _b === void 0 ? void 0 : _b.lastName}` ||
|
|
38
|
+
s.id,
|
|
39
|
+
value: s.id,
|
|
40
|
+
});
|
|
41
|
+
}));
|
|
42
|
+
const [studentOptionsLoading, setStudentOptionsLoading] = useState(false);
|
|
43
|
+
const debouncedStudentSearchQuery = useDebounce(studentSearchQuery, 300);
|
|
44
|
+
const trimmedStudentSearchQuery = debouncedStudentSearchQuery.trim();
|
|
45
|
+
const cachedStudentOptions = useMemo(() => cachedStudentProfiles.items
|
|
46
|
+
.filter((s) => {
|
|
47
|
+
var _a;
|
|
48
|
+
if (!familyId)
|
|
49
|
+
return true;
|
|
50
|
+
return ((_a = s.familyMember) === null || _a === void 0 ? void 0 : _a.familyId) === familyId;
|
|
51
|
+
})
|
|
52
|
+
.map((s) => {
|
|
53
|
+
var _a, _b;
|
|
54
|
+
return ({
|
|
55
|
+
label: `${s.computerNumber} - ${(_a = s.familyMember) === null || _a === void 0 ? void 0 : _a.firstName} ${(_b = s.familyMember) === null || _b === void 0 ? void 0 : _b.lastName}` ||
|
|
56
|
+
s.id,
|
|
57
|
+
value: s.id,
|
|
58
|
+
});
|
|
59
|
+
}), [cachedStudentProfiles.items, familyId]);
|
|
60
|
+
const selectedStudentOption = useMemo(() => {
|
|
61
|
+
var _a, _b;
|
|
62
|
+
const selected = cachedStudentProfiles.items.find((s) => s.id === studentProfileId);
|
|
63
|
+
return selected
|
|
64
|
+
? [
|
|
65
|
+
{
|
|
66
|
+
label: `${selected.computerNumber} - ${(_a = selected.familyMember) === null || _a === void 0 ? void 0 : _a.firstName} ${(_b = selected.familyMember) === null || _b === void 0 ? void 0 : _b.lastName}` ||
|
|
67
|
+
selected.id,
|
|
68
|
+
value: selected.id,
|
|
69
|
+
},
|
|
70
|
+
]
|
|
71
|
+
: [];
|
|
72
|
+
}, [cachedStudentProfiles.items, studentProfileId]);
|
|
73
|
+
const displayedStudentOptions = useMemo(() => {
|
|
74
|
+
const sourceOptions = trimmedStudentSearchQuery
|
|
75
|
+
? remoteStudentOptions
|
|
76
|
+
: cachedStudentOptions;
|
|
77
|
+
const mergedOptions = [...selectedStudentOption, ...sourceOptions];
|
|
78
|
+
const uniqueOptions = new Map(mergedOptions.map((option) => [option.value, option]));
|
|
79
|
+
return [...uniqueOptions.values()];
|
|
80
|
+
}, [
|
|
81
|
+
cachedStudentOptions,
|
|
82
|
+
remoteStudentOptions,
|
|
83
|
+
selectedStudentOption,
|
|
84
|
+
trimmedStudentSearchQuery,
|
|
85
|
+
]);
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (!trimmedStudentSearchQuery || !schoolId)
|
|
88
|
+
return;
|
|
89
|
+
let isActive = true;
|
|
90
|
+
const fetchStudents = async () => {
|
|
91
|
+
setStudentOptionsLoading(true);
|
|
92
|
+
try {
|
|
93
|
+
const { items } = await getCachedStudentProfiles({
|
|
94
|
+
params: {
|
|
95
|
+
schoolId,
|
|
96
|
+
searchQuery: trimmedStudentSearchQuery,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
if (!isActive)
|
|
100
|
+
return;
|
|
101
|
+
setRemoteStudentOptions((items || []).map((s) => {
|
|
102
|
+
var _a, _b;
|
|
103
|
+
return ({
|
|
104
|
+
label: `${s.computerNumber} - ${(_a = s.familyMember) === null || _a === void 0 ? void 0 : _a.firstName} ${(_b = s.familyMember) === null || _b === void 0 ? void 0 : _b.lastName}` ||
|
|
105
|
+
s.id,
|
|
106
|
+
value: s.id,
|
|
107
|
+
});
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
catch (_a) {
|
|
111
|
+
if (!isActive)
|
|
112
|
+
return;
|
|
113
|
+
setRemoteStudentOptions([]);
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
if (isActive) {
|
|
117
|
+
setStudentOptionsLoading(false);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
void fetchStudents();
|
|
122
|
+
return () => {
|
|
123
|
+
isActive = false;
|
|
124
|
+
};
|
|
125
|
+
}, [schoolId, trimmedStudentSearchQuery]);
|
|
126
|
+
const cachedFeeStructures = getCachedFeeStructuresSync();
|
|
127
|
+
const [feeStructureSearchQuery, setFeeStructureSearchQuery] = useState("");
|
|
128
|
+
const [remoteFeeStructureOptions, setRemoteFeeStructureOptions] = useState(() => cachedFeeStructures.items.map((fs) => ({
|
|
129
|
+
label: fs.name || fs.id,
|
|
130
|
+
value: fs.id,
|
|
131
|
+
})));
|
|
132
|
+
const [feeStructureOptionsLoading, setFeeStructureOptionsLoading] = useState(false);
|
|
133
|
+
const debouncedFeeStructureSearchQuery = useDebounce(feeStructureSearchQuery, 300);
|
|
134
|
+
const trimmedFeeStructureSearchQuery = debouncedFeeStructureSearchQuery.trim();
|
|
135
|
+
const cachedFeeStructureOptions = useMemo(() => cachedFeeStructures.items.map((fs) => ({
|
|
136
|
+
label: fs.name || fs.id,
|
|
137
|
+
value: fs.id,
|
|
138
|
+
})), [cachedFeeStructures.items]);
|
|
139
|
+
const selectedFeeStructureOption = useMemo(() => {
|
|
140
|
+
const selected = cachedFeeStructures.items.find((fs) => fs.id === feeStructureId);
|
|
141
|
+
return selected
|
|
142
|
+
? [{ label: selected.name || selected.id, value: selected.id }]
|
|
143
|
+
: [];
|
|
144
|
+
}, [cachedFeeStructures.items, feeStructureId]);
|
|
145
|
+
const displayedFeeStructureOptions = useMemo(() => {
|
|
146
|
+
const sourceOptions = trimmedFeeStructureSearchQuery
|
|
147
|
+
? remoteFeeStructureOptions
|
|
148
|
+
: cachedFeeStructureOptions;
|
|
149
|
+
const mergedOptions = [...selectedFeeStructureOption, ...sourceOptions];
|
|
150
|
+
const uniqueOptions = new Map(mergedOptions.map((option) => [option.value, option]));
|
|
151
|
+
return [...uniqueOptions.values()];
|
|
152
|
+
}, [
|
|
153
|
+
cachedFeeStructureOptions,
|
|
154
|
+
remoteFeeStructureOptions,
|
|
155
|
+
selectedFeeStructureOption,
|
|
156
|
+
trimmedFeeStructureSearchQuery,
|
|
157
|
+
]);
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
if (!trimmedFeeStructureSearchQuery || !schoolId)
|
|
160
|
+
return;
|
|
161
|
+
let isActive = true;
|
|
162
|
+
const fetchFeeStructures = async () => {
|
|
163
|
+
setFeeStructureOptionsLoading(true);
|
|
164
|
+
try {
|
|
165
|
+
const { items } = await getCachedFeeStructures({
|
|
166
|
+
params: {
|
|
167
|
+
schoolId,
|
|
168
|
+
searchQuery: trimmedFeeStructureSearchQuery,
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
if (!isActive)
|
|
172
|
+
return;
|
|
173
|
+
setRemoteFeeStructureOptions((items || []).map((fs) => ({
|
|
174
|
+
label: fs.name || fs.id,
|
|
175
|
+
value: fs.id,
|
|
176
|
+
})));
|
|
177
|
+
}
|
|
178
|
+
catch (_a) {
|
|
179
|
+
if (!isActive)
|
|
180
|
+
return;
|
|
181
|
+
setRemoteFeeStructureOptions([]);
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
if (isActive) {
|
|
185
|
+
setFeeStructureOptionsLoading(false);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
void fetchFeeStructures();
|
|
190
|
+
return () => {
|
|
191
|
+
isActive = false;
|
|
192
|
+
};
|
|
193
|
+
}, [schoolId, trimmedFeeStructureSearchQuery]);
|
|
29
194
|
const discountCodes = getCachedDiscountCodesSync();
|
|
30
195
|
const discountCodeOptions = (discountCodes.items || [])
|
|
31
196
|
.filter((code) => code.enabled)
|
|
@@ -34,22 +199,5 @@ export const StudentFeeForm = () => {
|
|
|
34
199
|
value: code.id,
|
|
35
200
|
description: code.description || undefined,
|
|
36
201
|
}));
|
|
37
|
-
return (_jsxs("div", { className: "
|
|
38
|
-
.filter((s) => {
|
|
39
|
-
var _a;
|
|
40
|
-
if (!familyId)
|
|
41
|
-
return true;
|
|
42
|
-
return ((_a = s.familyMember) === null || _a === void 0 ? void 0 : _a.familyId) === familyId;
|
|
43
|
-
})
|
|
44
|
-
.map((s) => {
|
|
45
|
-
var _a, _b;
|
|
46
|
-
return ({
|
|
47
|
-
label: `${s.computerNumber} - ${(_a = s.familyMember) === null || _a === void 0 ? void 0 : _a.firstName} ${(_b = s.familyMember) === null || _b === void 0 ? void 0 : _b.lastName}` ||
|
|
48
|
-
s.id,
|
|
49
|
-
value: s.id || "",
|
|
50
|
-
});
|
|
51
|
-
}), placeholder: t("formStudentPlaceholder"), required: true, searchPlaceholder: t("formSearchStudentsPlaceholder"), value: studentProfileId || "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoFeeStructureEmpty"), error: errors.feeStructureId, id: "feeStructureId", info: t("formFeeStructureInfo"), label: t("formFeeStructureLabel"), onValueChange: (value) => handleChange("feeStructureId", value), options: (getCachedFeeStructuresSync().items || []).map((fs) => ({
|
|
52
|
-
label: fs.name || fs.id,
|
|
53
|
-
value: fs.id,
|
|
54
|
-
})), placeholder: t("formFeeStructurePlaceholder"), required: true, searchPlaceholder: t("formSearchFeeStructuresPlaceholder"), value: feeStructureId || "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoDiscountCodeEmpty"), error: errors.discountCodeId, id: "discountCodeId", info: t("formDiscountCodeInfo"), label: t("formDiscountCodeLabel"), onValueChange: (value) => handleChange("discountCodeId", value), options: discountCodeOptions, placeholder: t("formDiscountCodePlaceholder"), searchPlaceholder: t("formSearchDiscountCodesPlaceholder"), value: discountCodeId || "" }), _jsx(EnhancedInput, { error: errors.discountAmount, id: "discountAmount", info: t("formDiscountAmountInfo"), label: t("formDiscountAmountLabel"), min: "0", onChange: (e) => handleChange("discountAmount", Number(e.target.value)), placeholder: "0.00", required: true, step: "0.01", type: "number", value: discountAmount || 0 }), _jsx(EnhancedInput, { error: errors.amount, id: "amount", info: t("formTotalAmountInfo"), label: t("formTotalAmountLabel"), min: "0", onChange: (e) => handleChange("amount", Number(e.target.value)), placeholder: "0.00", required: true, step: "0.01", type: "number", value: amount }), _jsx(EnhancedInput, { error: errors.amountPaid, id: "amountPaid", info: t("formAmountPaidInfo"), label: t("formAmountPaidLabel"), min: "0", onChange: (e) => handleChange("amountPaid", Number(e.target.value)), placeholder: "0.00", step: "0.01", type: "number", value: amountPaid }), _jsx(EnhancedInput, { disabled: true, error: errors.amountDue, id: "amountDue", info: t("formAmountDueInfo"), label: t("formAmountDueLabel"), min: "0", onChange: (e) => handleChange("amountDue", Number(e.target.value)), placeholder: "0.00", step: "0.01", type: "number", value: amountDue }), _jsx(EnhancedInput, { error: errors.dueDate, id: "dueDate", info: t("formDueDateInfo"), label: t("formDueDateLabel"), onChange: (e) => handleChange("dueDate", e.target.value), required: true, type: "date", value: formatDate(dueDate, DATE_FORMATS.YYYY_MM_DD) }), _jsx(EnhancedCombobox, { emptyText: t("formNoStatusEmpty"), error: errors.status, id: "status", info: t("formStatusInfo"), label: t("formOptionStatus"), onValueChange: (value) => handleChange("status", value), options: PAYMENT_STATUS_OPTIONS, placeholder: t("formStatusPlaceholder"), required: true, searchPlaceholder: t("formSearchStatusPlaceholder"), value: status })] }), _jsx(EnhancedTextarea, { error: errors.remarks, id: "remarks", label: t("formRemarksLabel"), onChange: (e) => handleChange("remarks", e.target.value), placeholder: t("formRemarksPlaceholder"), value: remarks || "" }), _jsx(EnhancedCheckbox, { checked: enabled, error: errors.enabled, id: "enabled", info: t("formEnabledInfo"), label: t("badgeActive"), onCheckedChange: (checked) => handleChange("enabled", !!checked) })] }));
|
|
202
|
+
return (_jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedCombobox, { emptyText: t("formNoStudentEmpty"), error: errors.studentProfileId, id: "studentProfileId", info: t("formStudentInfo"), label: t("formStudentLabel"), loading: studentOptionsLoading && Boolean(trimmedStudentSearchQuery), onSearchChange: setStudentSearchQuery, onValueChange: (value) => handleChange("studentProfileId", value), options: displayedStudentOptions, placeholder: t("formStudentPlaceholder"), required: true, searchPlaceholder: t("formSearchStudentsPlaceholder"), value: studentProfileId || "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoFeeStructureEmpty"), error: errors.feeStructureId, id: "feeStructureId", info: t("formFeeStructureInfo"), label: t("formFeeStructureLabel"), loading: feeStructureOptionsLoading && Boolean(trimmedFeeStructureSearchQuery), onSearchChange: setFeeStructureSearchQuery, onValueChange: (value) => handleChange("feeStructureId", value), options: displayedFeeStructureOptions, placeholder: t("formFeeStructurePlaceholder"), required: true, searchPlaceholder: t("formSearchFeeStructuresPlaceholder"), value: feeStructureId || "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoDiscountCodeEmpty"), error: errors.discountCodeId, id: "discountCodeId", info: t("formDiscountCodeInfo"), label: t("formDiscountCodeLabel"), onValueChange: (value) => handleChange("discountCodeId", value), options: discountCodeOptions, placeholder: t("formDiscountCodePlaceholder"), searchPlaceholder: t("formSearchDiscountCodesPlaceholder"), value: discountCodeId || "" }), _jsx(EnhancedInput, { error: errors.discountAmount, id: "discountAmount", info: t("formDiscountAmountInfo"), label: t("formDiscountAmountLabel"), min: "0", onChange: (e) => handleChange("discountAmount", Number(e.target.value)), placeholder: "0.00", required: true, step: "0.01", type: "number", value: discountAmount || 0 }), _jsx(EnhancedInput, { error: errors.amount, id: "amount", info: t("formTotalAmountInfo"), label: t("formTotalAmountLabel"), min: "0", onChange: (e) => handleChange("amount", Number(e.target.value)), placeholder: "0.00", required: true, step: "0.01", type: "number", value: amount }), _jsx(EnhancedInput, { error: errors.amountPaid, id: "amountPaid", info: t("formAmountPaidInfo"), label: t("formAmountPaidLabel"), min: "0", onChange: (e) => handleChange("amountPaid", Number(e.target.value)), placeholder: "0.00", step: "0.01", type: "number", value: amountPaid }), _jsx(EnhancedInput, { disabled: true, error: errors.amountDue, id: "amountDue", info: t("formAmountDueInfo"), label: t("formAmountDueLabel"), min: "0", onChange: (e) => handleChange("amountDue", Number(e.target.value)), placeholder: "0.00", step: "0.01", type: "number", value: amountDue }), _jsx(EnhancedInput, { error: errors.dueDate, id: "dueDate", info: t("formDueDateInfo"), label: t("formDueDateLabel"), onChange: (e) => handleChange("dueDate", e.target.value), required: true, type: "date", value: formatDate(dueDate, DATE_FORMATS.YYYY_MM_DD) }), _jsx(EnhancedCombobox, { emptyText: t("formNoStatusEmpty"), error: errors.status, id: "status", info: t("formStatusInfo"), label: t("formOptionStatus"), onValueChange: (value) => handleChange("status", value), options: PAYMENT_STATUS_OPTIONS, placeholder: t("formStatusPlaceholder"), required: true, searchPlaceholder: t("formSearchStatusPlaceholder"), value: status }), _jsx(EnhancedTextarea, { error: errors.remarks, id: "remarks", label: t("formRemarksLabel"), onChange: (e) => handleChange("remarks", e.target.value), placeholder: t("formRemarksPlaceholder"), value: remarks || "" }), _jsx(EnhancedCheckbox, { checked: enabled, error: errors.enabled, id: "enabled", info: t("formEnabledInfo"), label: t("badgeActive"), onCheckedChange: (checked) => handleChange("enabled", !!checked) })] }));
|
|
55
203
|
};
|