@appcorp/fusion-storybook 0.4.21 → 0.4.23

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.
@@ -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.map((f) => (Object.assign(Object.assign({}, f), { status: state.status }))),
547
- amountPaid: Number(state.amountPaid) || 0,
548
- paidAt: state.paidAt || null,
549
- remarks: state.remarks || "",
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, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
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 accruedFine = fees.reduce((sum, f) => { var _a; return sum + ((_a = f.fine) !== null && _a !== void 0 ? _a : 0); }, 0);
69
- const total = accumulatedAmount + accruedFine;
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: "studentFeesSingleEntry",
74
- value: feeByComputerNumber.studentFees,
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, accumulatedAmount, state.paidAt]);
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 fee = (_f = (_e = response === null || response === void 0 ? void 0 : response.studentFees) === null || _e === void 0 ? void 0 : _e[0]) !== null && _f !== void 0 ? _f : null;
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: false },
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 accumulatedFine = useMemo(() => {
131
- var _a;
132
- if (!((_a = response === null || response === void 0 ? void 0 : response.studentFees) === null || _a === void 0 ? void 0 : _a.length))
133
- return 0;
134
- return response.studentFees.reduce((sum, f) => { var _a; return sum + ((_a = f.fine) !== null && _a !== void 0 ? _a : 0); }, 0);
135
- }, [response]);
136
- const handleUpdateAmountPaid = (value) => {
137
- const parsed = parseFloat(value);
138
- if (isNaN(parsed))
139
- return;
140
- handleChange("amountPaid", parsed);
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(_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: accumulatedFine, type: "number", step: "0.01" }), _jsx(EnhancedInput, { readOnly: true, id: "amount-readonly", info: t("formTotalAmountInfo"), label: t("formTotalAmountLabel"), value: accumulatedAmount + (accumulatedFine !== null && accumulatedFine !== void 0 ? accumulatedFine : 0), type: "number", step: "0.01" }), _jsx(EnhancedInput, { id: "amountPaid", info: t("formAmountPaidInfo"), label: t("formAmountPaidLabel"), value: state.amountPaid || 0, onChange: (e) => handleUpdateAmountPaid(e.target.value), type: "number", step: "0.01" }), _jsx(EnhancedInput, { readOnly: true, id: "amountDue-readonly", info: t("formAmountDueInfo"), label: t("formAmountDueLabel"), value: (accumulatedAmount !== null && accumulatedAmount !== void 0 ? accumulatedAmount : 0) +
166
- (accumulatedFine !== null && accumulatedFine !== void 0 ? accumulatedFine : 0) -
167
- Number(state.amountPaid || 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] }));
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.drawerMisc1Description,
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appcorp/fusion-storybook",
3
- "version": "0.4.21",
3
+ "version": "0.4.23",
4
4
  "scripts": {
5
5
  "build-storybook": "storybook build",
6
6
  "build:next": "next build",
@@ -37,7 +37,7 @@
37
37
  ]
38
38
  },
39
39
  "devDependencies": {
40
- "@appcorp/shadcn": "^2.0.15",
40
+ "@appcorp/shadcn": "^2.0.17",
41
41
  "@chromatic-com/storybook": "^5.1.2",
42
42
  "@commitlint/cli": "^20.5.0",
43
43
  "@commitlint/config-conventional": "^20.5.0",