@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.
@@ -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
- 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"), onValueChange: (value) => handleChange("category", value), options: EXPENSE_CATEGORY_OPTIONS, 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
+ 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 classOptions = (getCachedClassesSync().items || [])
24
- .filter((c) => c.enabled)
25
- .map((c) => ({ label: `${c.name} (${c.code})`, value: c.id }));
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: (_b = (_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.currency) !== null && _b !== void 0 ? _b : "",
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: classOptions, 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) })] }));
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: "space-y-4", children: [_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"), onValueChange: (value) => handleChange("studentProfileId", value), options: (getCachedStudentProfilesSync().items || [])
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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appcorp/fusion-storybook",
3
- "version": "0.1.95",
3
+ "version": "0.1.96",
4
4
  "scripts": {
5
5
  "build-storybook": "storybook build",
6
6
  "build:next": "next build",