@appcorp/fusion-storybook 0.3.74 → 0.3.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/base-modules/student-fee/context/shared.d.ts +2 -2
- package/base-modules/student-fee/context/shared.js +2 -2
- package/base-modules/student-fee/context/use-student-fee-module.js +2 -2
- package/base-modules/student-fee/monthly-create.js +1 -1
- package/base-modules/student-fee/msic-1.d.ts +8 -0
- package/base-modules/student-fee/msic-1.js +33 -0
- package/base-modules/student-fee/msic-2.d.ts +8 -0
- package/base-modules/student-fee/msic-2.js +122 -0
- package/base-modules/student-fee/page.js +6 -4
- package/base-modules/student-fee/single-entry.js +1 -1
- package/package.json +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -4,8 +4,8 @@ export declare const STUDENT_FEE_DRAWER: {
|
|
|
4
4
|
readonly FORM_DRAWER: string;
|
|
5
5
|
readonly MORE_ACTIONS_DRAWER: string;
|
|
6
6
|
readonly VIEW_DRAWER: string;
|
|
7
|
-
readonly
|
|
8
|
-
readonly
|
|
7
|
+
readonly MISC_DRAWER_1: string;
|
|
8
|
+
readonly MISC_DRAWER_2: string;
|
|
9
9
|
};
|
|
10
10
|
export interface StudentFeesListResponse {
|
|
11
11
|
items: StudentFeeBE[];
|
|
@@ -11,8 +11,8 @@ export const STUDENT_FEE_DRAWER = {
|
|
|
11
11
|
FORM_DRAWER: DRAWER_TYPES.FORM_DRAWER,
|
|
12
12
|
MORE_ACTIONS_DRAWER: DRAWER_TYPES.MORE_ACTIONS_DRAWER,
|
|
13
13
|
VIEW_DRAWER: DRAWER_TYPES.VIEW_DRAWER,
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
MISC_DRAWER_1: DRAWER_TYPES.MISC_DRAWER_1,
|
|
15
|
+
MISC_DRAWER_2: DRAWER_TYPES.MISC_DRAWER_2,
|
|
16
16
|
};
|
|
17
17
|
const studentFeeConfig = {
|
|
18
18
|
name: "StudentFee",
|
|
@@ -460,7 +460,7 @@ export const useStudentFeeModule = () => {
|
|
|
460
460
|
resetRecordFormState();
|
|
461
461
|
dispatch({
|
|
462
462
|
type: STUDENT_FEE_ACTION_TYPES.SET_DRAWER,
|
|
463
|
-
payload: { drawer: STUDENT_FEE_DRAWER.
|
|
463
|
+
payload: { drawer: STUDENT_FEE_DRAWER.MISC_DRAWER_1 },
|
|
464
464
|
});
|
|
465
465
|
}, [dispatch, resetRecordFormState]);
|
|
466
466
|
const handleCreateMonthly = useCallback(() => {
|
|
@@ -471,7 +471,7 @@ export const useStudentFeeModule = () => {
|
|
|
471
471
|
});
|
|
472
472
|
dispatch({
|
|
473
473
|
type: STUDENT_FEE_ACTION_TYPES.SET_DRAWER,
|
|
474
|
-
payload: { drawer: STUDENT_FEE_DRAWER.
|
|
474
|
+
payload: { drawer: STUDENT_FEE_DRAWER.MISC_DRAWER_2 },
|
|
475
475
|
});
|
|
476
476
|
}, [dispatch, resetRecordFormState]);
|
|
477
477
|
const handleView = useCallback((row) => {
|
|
@@ -10,6 +10,6 @@ export const StudentFeeCreateMonthly = () => {
|
|
|
10
10
|
const { closeDrawer, handleProceedMonthlyCreation, state, updateLoading } = useStudentFeeModule();
|
|
11
11
|
const t = useTranslations("studentFee");
|
|
12
12
|
const { disableSaveButton, drawer, errors } = state;
|
|
13
|
-
const isOpen = drawer === STUDENT_FEE_DRAWER.
|
|
13
|
+
const isOpen = drawer === STUDENT_FEE_DRAWER.MISC_DRAWER_1;
|
|
14
14
|
return (_jsx(DrawerGeneric, { filterDrawer: null, filterDrawerHeader: null, filterDrawerFooter: null, formDrawer: isOpen ? _jsx(StudentFeeMonthlyForm, {}) : null, formDrawerHeader: isOpen ? (_jsx(EnhancedDrawerHeader, { title: t("drawerCreateMonthlyTitle"), description: t("drawerCreateMonthlyDescription") })) : null, formDrawerFooter: isOpen ? (_jsx(EnhancedDrawerFooter, { applyFilters: () => { }, clearFilters: () => { }, closeDrawer: closeDrawer, disableSaveButton: disableSaveButton, drawerType: DRAWER_FOOTER_COMPONENT_TYPE.FORM_DRAWER, errors: errors, handleSubmit: handleProceedMonthlyCreation, updateLoading: updateLoading })) : null, viewDrawer: null, viewDrawerHeader: null, viewDrawerFooter: null, moreActionsDrawer: null, moreActionsDrawerHeader: null, moreActionsDrawerFooter: null, onOpenChange: (open) => !open && closeDrawer(), open: isOpen }));
|
|
15
15
|
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* StudentFee Filter
|
|
5
|
+
*
|
|
6
|
+
* Filter UI for the student-fee list. Supports filterEnabled (radio) and
|
|
7
|
+
* filterStatus (payment status combobox).
|
|
8
|
+
*/
|
|
9
|
+
import { useCallback, useState } from "react";
|
|
10
|
+
import { STUDENT_FEE_ACTION_TYPES, useStudentFeeModule } from "./context";
|
|
11
|
+
import { useTranslations } from "next-intl";
|
|
12
|
+
import { EnhancedCheckbox } from "@appcorp/shadcn/components/enhanced-checkbox";
|
|
13
|
+
const CHECKBOX_KEYS = [
|
|
14
|
+
"formMonthlyCheckStudentProfile",
|
|
15
|
+
"formMonthlyCheckFeeStructure",
|
|
16
|
+
"formMonthlyCheckGmail",
|
|
17
|
+
"formMonthlyCheckReady",
|
|
18
|
+
];
|
|
19
|
+
export const StudentFeeMonthly = () => {
|
|
20
|
+
const t = useTranslations("studentFee");
|
|
21
|
+
const { dispatch } = useStudentFeeModule();
|
|
22
|
+
const [checked, setChecked] = useState(Object.fromEntries(CHECKBOX_KEYS.map((key) => [key, false])));
|
|
23
|
+
// const allChecked = CHECKBOX_KEYS.every((key) => checked[key]);
|
|
24
|
+
const handleCheckChange = useCallback((key, value) => {
|
|
25
|
+
const next = Object.assign(Object.assign({}, checked), { [key]: value });
|
|
26
|
+
setChecked(next);
|
|
27
|
+
dispatch({
|
|
28
|
+
type: STUDENT_FEE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
|
|
29
|
+
payload: { disabled: !CHECKBOX_KEYS.every((k) => next[k]) },
|
|
30
|
+
});
|
|
31
|
+
}, [checked, dispatch]);
|
|
32
|
+
return (_jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx("p", { className: "text-foreground text-sm", children: t("formMonthlyWarningMessage") }), _jsx("div", { className: "flex flex-col gap-3", children: CHECKBOX_KEYS.map((key) => (_jsx(EnhancedCheckbox, { checked: checked[key], id: key, label: t(key), onCheckedChange: (value) => handleCheckChange(key, value) }, key))) })] }));
|
|
33
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* StudentFee Filter
|
|
5
|
+
*
|
|
6
|
+
* Filter UI for the student-fee list. Supports filterEnabled (radio) and
|
|
7
|
+
* filterStatus (payment status combobox).
|
|
8
|
+
*/
|
|
9
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
10
|
+
import { STUDENT_FEE_ACTION_TYPES, useStudentFeeModule } from "./context";
|
|
11
|
+
import { useTranslations } from "next-intl";
|
|
12
|
+
import { EnhancedTextarea } from "@appcorp/shadcn/components/enhanced-textarea";
|
|
13
|
+
import { EnhancedInput } from "@appcorp/shadcn/components/enhanced-input";
|
|
14
|
+
import { getCachedWorkspaceSync } from "../workspace/cache";
|
|
15
|
+
import { useDebounce } from "@react-pakistan/util-functions/hooks/use-debounce";
|
|
16
|
+
import { useFetch } from "@react-pakistan/util-functions/hooks/use-fetch";
|
|
17
|
+
import { STUDENT_FEE_API_ROUTES } from "@/constants";
|
|
18
|
+
import { API_METHODS } from "@react-pakistan/util-functions/constants/api-methods";
|
|
19
|
+
import { DISCOUNT_TYPE } from "../../types";
|
|
20
|
+
import { Loader2 } from "lucide-react";
|
|
21
|
+
export const StudentFeeSingle = () => {
|
|
22
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
23
|
+
const { dispatch } = useStudentFeeModule();
|
|
24
|
+
// const { state } = useStudentFeeModule();
|
|
25
|
+
const workspace = getCachedWorkspaceSync();
|
|
26
|
+
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 : "";
|
|
27
|
+
const t = useTranslations("studentFee");
|
|
28
|
+
// const [amountPaid, setAmountPaid] = useState("");
|
|
29
|
+
// const [remarks, setRemarks] = useState("");
|
|
30
|
+
const [computerNumber, setComputerNumber] = useState("");
|
|
31
|
+
const [amountPaid, setAmountPaid] = useState("");
|
|
32
|
+
const [paidAt, setPaidAt] = useState(() => {
|
|
33
|
+
const d = new Date();
|
|
34
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
35
|
+
});
|
|
36
|
+
const [remarks, setRemarks] = useState("");
|
|
37
|
+
const debouncedComputerNumber = useDebounce(computerNumber, 800);
|
|
38
|
+
const { fetchNow: fetchByComputerNumber, data: feeByComputerNumber, loading: fetchingByCN, } = useFetch(STUDENT_FEE_API_ROUTES.BY_COMPUTER_NUMBER, {
|
|
39
|
+
method: API_METHODS.GET,
|
|
40
|
+
});
|
|
41
|
+
const fetchByCNRef = useRef(fetchByComputerNumber);
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
fetchByCNRef.current = fetchByComputerNumber;
|
|
44
|
+
}, [fetchByComputerNumber]);
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
var _a;
|
|
47
|
+
if (!debouncedComputerNumber)
|
|
48
|
+
return;
|
|
49
|
+
fetchByCNRef.current(undefined, {
|
|
50
|
+
params: {
|
|
51
|
+
computerNumber: debouncedComputerNumber,
|
|
52
|
+
schoolId: (_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}, [debouncedComputerNumber, (_c = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _c === void 0 ? void 0 : _c.id]);
|
|
56
|
+
const isStale = computerNumber !== debouncedComputerNumber;
|
|
57
|
+
const hasData = !!feeByComputerNumber &&
|
|
58
|
+
!!debouncedComputerNumber &&
|
|
59
|
+
!isStale &&
|
|
60
|
+
!fetchingByCN;
|
|
61
|
+
const response = hasData
|
|
62
|
+
? feeByComputerNumber
|
|
63
|
+
: null;
|
|
64
|
+
const profile = (_d = response === null || response === void 0 ? void 0 : response.studentProfile) !== null && _d !== void 0 ? _d : null;
|
|
65
|
+
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;
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
dispatch({
|
|
68
|
+
type: STUDENT_FEE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
|
|
69
|
+
payload: { disabled: false },
|
|
70
|
+
});
|
|
71
|
+
}, [fee, dispatch]);
|
|
72
|
+
const paymentStatusLabelMap = {
|
|
73
|
+
PENDING: t("formOptionPending"),
|
|
74
|
+
PARTIAL: t("formOptionPartial"),
|
|
75
|
+
PAID: t("formOptionPaid"),
|
|
76
|
+
OVERDUE: t("formOptionOverdue"),
|
|
77
|
+
CANCELLED: t("formOptionCancelled"),
|
|
78
|
+
REFUNDED: t("formOptionRefunded"),
|
|
79
|
+
};
|
|
80
|
+
const studentName = profile
|
|
81
|
+
? `${profile.firstName} ${profile.lastName}`.trim() || profile.id
|
|
82
|
+
: "";
|
|
83
|
+
const feeStructureName = ((_g = fee === null || fee === void 0 ? void 0 : fee.feeStructure) === null || _g === void 0 ? void 0 : _g.name) || (fee === null || fee === void 0 ? void 0 : fee.feeStructureId) || "";
|
|
84
|
+
const discountCodeLabel = (fee === null || fee === void 0 ? void 0 : fee.discountCode)
|
|
85
|
+
? `${fee.discountCode.code} - ${fee.discountCode.discountValue}${fee.discountCode.discountType === DISCOUNT_TYPE.PERCENTAGE ? "%" : currency}`
|
|
86
|
+
: (fee === null || fee === void 0 ? void 0 : fee.discountCodeId) || "";
|
|
87
|
+
const statusDisplay = (fee === null || fee === void 0 ? void 0 : fee.status)
|
|
88
|
+
? paymentStatusLabelMap[fee.status] || fee.status
|
|
89
|
+
: "";
|
|
90
|
+
const formatDisplayDate = (rawDate) => {
|
|
91
|
+
if (!rawDate)
|
|
92
|
+
return "";
|
|
93
|
+
const d = new Date(rawDate);
|
|
94
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
95
|
+
};
|
|
96
|
+
const accumulatedAmount = useMemo(() => {
|
|
97
|
+
var _a;
|
|
98
|
+
if (!((_a = response === null || response === void 0 ? void 0 : response.studentFees) === null || _a === void 0 ? void 0 : _a.length))
|
|
99
|
+
return 0;
|
|
100
|
+
return response.studentFees.reduce((sum, f) => { var _a; return sum + ((_a = f.amount) !== null && _a !== void 0 ? _a : 0); }, 0);
|
|
101
|
+
}, [response]);
|
|
102
|
+
const accumulatedFine = useMemo(() => {
|
|
103
|
+
var _a;
|
|
104
|
+
if (!((_a = response === null || response === void 0 ? void 0 : response.studentFees) === null || _a === void 0 ? void 0 : _a.length))
|
|
105
|
+
return 0;
|
|
106
|
+
return response.studentFees.reduce((sum, f) => { var _a; return sum + ((_a = f.fine) !== null && _a !== void 0 ? _a : 0); }, 0);
|
|
107
|
+
}, [response]);
|
|
108
|
+
const handleUpdateAmountPaid = (value) => {
|
|
109
|
+
const amountPaid = parseFloat(value);
|
|
110
|
+
if (isNaN(amountPaid))
|
|
111
|
+
return;
|
|
112
|
+
setAmountPaid(value);
|
|
113
|
+
};
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
(async () => {
|
|
116
|
+
setAmountPaid(String((accumulatedAmount !== null && accumulatedAmount !== void 0 ? accumulatedAmount : 0) + (accumulatedFine !== null && accumulatedFine !== void 0 ? accumulatedFine : 0)));
|
|
117
|
+
})();
|
|
118
|
+
}, [fee, accumulatedFine, accumulatedAmount, setAmountPaid]);
|
|
119
|
+
return (_jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedInput, { id: "computerNumber", info: t("formComputerNumberInfo"), label: t("formComputerNumberLabel"), onChange: (e) => setComputerNumber(e.target.value), placeholder: t("formComputerNumberPlaceholder"), type: "number", required: true, value: 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: accumulatedAmount + (accumulatedFine !== null && accumulatedFine !== void 0 ? accumulatedFine : 0) - fee.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) +
|
|
120
|
+
(accumulatedFine !== null && accumulatedFine !== void 0 ? accumulatedFine : 0) -
|
|
121
|
+
parseFloat(amountPaid || "0"), type: "number", step: "0.01" }), _jsx(EnhancedInput, { readOnly: true, id: "dueDate-readonly", label: t("formDueDateLabel"), value: formatDisplayDate(fee === null || fee === void 0 ? void 0 : fee.dueDate), type: "date" }), _jsx(EnhancedInput, { id: "paidAt", info: t("formPaidAtInfo"), label: t("formPaidAtLabel"), onChange: (e) => setPaidAt(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: paidAt || "" }), _jsx(EnhancedInput, { readOnly: true, id: "status-readonly", label: t("formStatusLabel"), value: statusDisplay }), _jsx(EnhancedTextarea, { id: "remarks-readonly", label: t("formRemarksLabel"), value: remarks || "", onChange: (e) => setRemarks(e.target.value) })] })) : null] }));
|
|
122
|
+
};
|
|
@@ -23,11 +23,13 @@ import { StudentFeeFilter } from "./filter";
|
|
|
23
23
|
import { StudentFeeForm } from "./form";
|
|
24
24
|
import { StudentFeeMoreActions } from "./more-actions";
|
|
25
25
|
import { StudentFeeView } from "./view";
|
|
26
|
-
import { StudentFeeSingleEntry } from "./single-entry";
|
|
27
|
-
import { StudentFeeCreateMonthly } from "./monthly-create";
|
|
26
|
+
// import { StudentFeeSingleEntry } from "./single-entry";
|
|
27
|
+
// import { StudentFeeCreateMonthly } from "./monthly-create";
|
|
28
28
|
import { resolveRbacPermissions } from "../../utils/resolve-rbac-permissions";
|
|
29
29
|
import { RbacNoAccess } from "../../components/rbac-no-access";
|
|
30
30
|
import { formatStudentName, formatAmount, formatAmountPaid, formatAmountDue, } from "./constants";
|
|
31
|
+
import { StudentFeeMonthly } from "./msic-1";
|
|
32
|
+
import { StudentFeeSingle } from "./msic-2";
|
|
31
33
|
// ============================================================================
|
|
32
34
|
// TABLE COLUMN CONFIGURATION (static — formatters imported from constants)
|
|
33
35
|
// ============================================================================
|
|
@@ -75,8 +77,8 @@ const createComponentInstances = () => ({
|
|
|
75
77
|
form: _jsx(StudentFeeForm, {}),
|
|
76
78
|
moreActions: _jsx(StudentFeeMoreActions, {}),
|
|
77
79
|
view: _jsx(StudentFeeView, {}),
|
|
78
|
-
misc1: _jsx(
|
|
79
|
-
misc2: _jsx(
|
|
80
|
+
misc1: _jsx(StudentFeeMonthly, {}),
|
|
81
|
+
misc2: _jsx(StudentFeeSingle, {}),
|
|
80
82
|
});
|
|
81
83
|
const createStudentFeeConfig = ({ dispatch, drawerButtonCancel, drawerButtonSave, drawerFilterDescription, drawerFilterTitle, drawerFormDescription, drawerFormTitle, drawerMisc1Description, drawerMisc1Title, drawerMisc2Description, drawerMisc2Title, drawerMoreActionsDescription, drawerMoreActionsTitle, drawerViewDescription, drawerViewTitle, tableColumnHeaderActions, tableColumnHeaderAmount, tableColumnHeaderAmountDue, tableColumnHeaderAmountPaid, tableColumnHeaderDueDate, tableColumnHeaderFeeStructure, tableColumnHeaderId, tableColumnHeaderRemarks, tableColumnHeaderStatus, tableColumnHeaderStudent, tableDescription, tableSearchPlaceholder, tableTitle, }) => {
|
|
82
84
|
const components = createComponentInstances();
|
|
@@ -20,7 +20,7 @@ export const StudentFeeSingleEntry = () => {
|
|
|
20
20
|
const [remarks, setRemarks] = useState("");
|
|
21
21
|
const t = useTranslations("studentFee");
|
|
22
22
|
const { disableSaveButton, drawer, errors } = state;
|
|
23
|
-
const isOpen = drawer === STUDENT_FEE_DRAWER.
|
|
23
|
+
const isOpen = drawer === STUDENT_FEE_DRAWER.MISC_DRAWER_2;
|
|
24
24
|
const { fetchNow: fetchByComputerNumber, data: feeByComputerNumber, loading: fetchingByCN, } = useFetch(STUDENT_FEE_API_ROUTES.BY_COMPUTER_NUMBER, {
|
|
25
25
|
method: API_METHODS.GET,
|
|
26
26
|
});
|