@appcorp/fusion-storybook 0.1.4

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.
Files changed (131) hide show
  1. package/README.md +147 -0
  2. package/base-modules/admission/cache.d.ts +14 -0
  3. package/base-modules/admission/cache.js +31 -0
  4. package/base-modules/admission/constants.d.ts +32 -0
  5. package/base-modules/admission/constants.js +37 -0
  6. package/base-modules/admission/context.d.ts +369 -0
  7. package/base-modules/admission/context.js +755 -0
  8. package/base-modules/admission/filter.d.ts +1 -0
  9. package/base-modules/admission/filter.js +31 -0
  10. package/base-modules/admission/form.d.ts +1 -0
  11. package/base-modules/admission/form.js +86 -0
  12. package/base-modules/admission/more-actions.d.ts +1 -0
  13. package/base-modules/admission/more-actions.js +5 -0
  14. package/base-modules/admission/page.d.ts +16 -0
  15. package/base-modules/admission/page.js +128 -0
  16. package/base-modules/admission/validate.d.ts +38 -0
  17. package/base-modules/admission/validate.js +70 -0
  18. package/base-modules/admission/view.d.ts +1 -0
  19. package/base-modules/admission/view.js +40 -0
  20. package/base-modules/discount-code/cache.d.ts +27 -0
  21. package/base-modules/discount-code/cache.js +46 -0
  22. package/base-modules/discount-code/constants.d.ts +19 -0
  23. package/base-modules/discount-code/constants.js +26 -0
  24. package/base-modules/discount-code/context.d.ts +154 -0
  25. package/base-modules/discount-code/context.js +416 -0
  26. package/base-modules/discount-code/filter.d.ts +9 -0
  27. package/base-modules/discount-code/filter.js +14 -0
  28. package/base-modules/discount-code/form.d.ts +1 -0
  29. package/base-modules/discount-code/form.js +44 -0
  30. package/base-modules/discount-code/more-actions.d.ts +9 -0
  31. package/base-modules/discount-code/more-actions.js +13 -0
  32. package/base-modules/discount-code/page.d.ts +16 -0
  33. package/base-modules/discount-code/page.js +120 -0
  34. package/base-modules/discount-code/validate.d.ts +19 -0
  35. package/base-modules/discount-code/validate.js +38 -0
  36. package/base-modules/discount-code/view.d.ts +1 -0
  37. package/base-modules/discount-code/view.js +26 -0
  38. package/base-modules/family/cache.d.ts +14 -0
  39. package/base-modules/family/cache.js +31 -0
  40. package/base-modules/family/constants.d.ts +22 -0
  41. package/base-modules/family/constants.js +26 -0
  42. package/base-modules/family/context.d.ts +173 -0
  43. package/base-modules/family/context.js +441 -0
  44. package/base-modules/family/filter.d.ts +1 -0
  45. package/base-modules/family/filter.js +28 -0
  46. package/base-modules/family/form.d.ts +1 -0
  47. package/base-modules/family/form.js +12 -0
  48. package/base-modules/family/more-actions.d.ts +1 -0
  49. package/base-modules/family/more-actions.js +5 -0
  50. package/base-modules/family/page.d.ts +16 -0
  51. package/base-modules/family/page.js +120 -0
  52. package/base-modules/family/validate.d.ts +15 -0
  53. package/base-modules/family/validate.js +18 -0
  54. package/base-modules/family/view.d.ts +1 -0
  55. package/base-modules/family/view.js +18 -0
  56. package/base-modules/family-member/cache.d.ts +14 -0
  57. package/base-modules/family-member/cache.js +31 -0
  58. package/base-modules/family-member/constants.d.ts +22 -0
  59. package/base-modules/family-member/constants.js +26 -0
  60. package/base-modules/family-member/context.d.ts +215 -0
  61. package/base-modules/family-member/context.js +486 -0
  62. package/base-modules/family-member/filter.d.ts +1 -0
  63. package/base-modules/family-member/filter.js +28 -0
  64. package/base-modules/family-member/form.d.ts +1 -0
  65. package/base-modules/family-member/form.js +17 -0
  66. package/base-modules/family-member/more-actions.d.ts +1 -0
  67. package/base-modules/family-member/more-actions.js +5 -0
  68. package/base-modules/family-member/page.d.ts +16 -0
  69. package/base-modules/family-member/page.js +122 -0
  70. package/base-modules/family-member/validate.d.ts +24 -0
  71. package/base-modules/family-member/validate.js +27 -0
  72. package/base-modules/family-member/view.d.ts +1 -0
  73. package/base-modules/family-member/view.js +40 -0
  74. package/base-modules/student-profile/cache.d.ts +14 -0
  75. package/base-modules/student-profile/cache.js +31 -0
  76. package/base-modules/student-profile/constants.d.ts +105 -0
  77. package/base-modules/student-profile/constants.js +132 -0
  78. package/base-modules/student-profile/context.d.ts +290 -0
  79. package/base-modules/student-profile/context.js +583 -0
  80. package/base-modules/student-profile/filter.d.ts +1 -0
  81. package/base-modules/student-profile/filter.js +26 -0
  82. package/base-modules/student-profile/form.d.ts +1 -0
  83. package/base-modules/student-profile/form.js +19 -0
  84. package/base-modules/student-profile/more-actions.d.ts +1 -0
  85. package/base-modules/student-profile/more-actions.js +5 -0
  86. package/base-modules/student-profile/page.d.ts +9 -0
  87. package/base-modules/student-profile/page.js +86 -0
  88. package/base-modules/student-profile/validate.d.ts +23 -0
  89. package/base-modules/student-profile/validate.js +34 -0
  90. package/base-modules/student-profile/view.d.ts +1 -0
  91. package/base-modules/student-profile/view.js +29 -0
  92. package/base-modules/workspace/cache.d.ts +9 -0
  93. package/base-modules/workspace/cache.js +28 -0
  94. package/base-modules/workspace/constants.d.ts +10 -0
  95. package/base-modules/workspace/constants.js +18 -0
  96. package/base-modules/workspace/context.d.ts +187 -0
  97. package/base-modules/workspace/context.js +293 -0
  98. package/base-modules/workspace/drawer.d.ts +1 -0
  99. package/base-modules/workspace/drawer.js +24 -0
  100. package/base-modules/workspace/filter.d.ts +1 -0
  101. package/base-modules/workspace/filter.js +13 -0
  102. package/base-modules/workspace/form.d.ts +1 -0
  103. package/base-modules/workspace/form.js +40 -0
  104. package/base-modules/workspace/more-actions.d.ts +1 -0
  105. package/base-modules/workspace/more-actions.js +13 -0
  106. package/base-modules/workspace/page.d.ts +1 -0
  107. package/base-modules/workspace/page.js +31 -0
  108. package/base-modules/workspace/validate.d.ts +9 -0
  109. package/base-modules/workspace/validate.js +8 -0
  110. package/base-modules/workspace/view.d.ts +1 -0
  111. package/base-modules/workspace/view.js +52 -0
  112. package/components/rbac-no-access.d.ts +6 -0
  113. package/components/rbac-no-access.js +4 -0
  114. package/constants.d.ts +81 -0
  115. package/constants.js +81 -0
  116. package/lib/utils.d.ts +2 -0
  117. package/lib/utils.js +5 -0
  118. package/package.json +104 -0
  119. package/tsconfig.build.tsbuildinfo +1 -0
  120. package/type.d.ts +1141 -0
  121. package/type.js +427 -0
  122. package/utils/admission-pdf.d.ts +78 -0
  123. package/utils/admission-pdf.js +73 -0
  124. package/utils/clear-cache.d.ts +1 -0
  125. package/utils/clear-cache.js +8 -0
  126. package/utils/format-value.d.ts +1 -0
  127. package/utils/format-value.js +1 -0
  128. package/utils/pdf-generator.d.ts +41 -0
  129. package/utils/pdf-generator.js +107 -0
  130. package/utils/resolve-rbac-permissions.d.ts +11 -0
  131. package/utils/resolve-rbac-permissions.js +251 -0
@@ -0,0 +1 @@
1
+ export declare const AdmissionFilter: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,31 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * Admission Filter Component
5
+ *
6
+ * Filter UI for admission records. Uses the module state pattern:
7
+ * `const { state, handleChange, handleSetStartDate, handleSetEndDate } = useAdmissionModule()`
8
+ */
9
+ import { EnhancedCombobox } from "@appcorp/shadcn/components/enhanced-combobox";
10
+ import { EnhancedRadio } from "@appcorp/shadcn/components/enhanced-radio";
11
+ import { useTranslations } from "next-intl";
12
+ import { ADMISSION_STATUS_OPTIONS } from "./constants";
13
+ import { useAdmissionModule } from "./context";
14
+ export const AdmissionFilter = () => {
15
+ const { state, handleChange, handleSetStartDate, handleSetEndDate } = useAdmissionModule();
16
+ const { filterEnabled, filterAdmissionStatus, filterStartDate, filterEndDate, } = state;
17
+ const t = useTranslations("admission");
18
+ const filterEnabledValue = filterEnabled === undefined
19
+ ? "undefined"
20
+ : filterEnabled
21
+ ? "true"
22
+ : "false";
23
+ return (_jsx("div", { className: "space-y-4", children: _jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedCombobox, { id: "filterAdmissionStatus", label: t("admissionStatus"), info: t("filterByAdmissionStatus"), options: [...ADMISSION_STATUS_OPTIONS], value: filterAdmissionStatus !== null && filterAdmissionStatus !== void 0 ? filterAdmissionStatus : undefined, onValueChange: (value) => handleChange("filterAdmissionStatus", value) }), _jsxs("div", { children: [_jsx("label", { htmlFor: "startDate", className: "text-foreground mb-1 block text-sm font-medium", children: t("startDate") }), _jsx("input", { id: "startDate", type: "date", value: filterStartDate || "", onChange: (e) => handleSetStartDate(e.target.value), className: "border-input bg-background text-foreground focus:ring-ring w-full rounded-md border px-3 py-2 focus:border-transparent focus:ring-2 focus:outline-none" })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "endDate", className: "text-foreground mb-1 block text-sm font-medium", children: t("endDate") }), _jsx("input", { id: "endDate", type: "date", value: filterEndDate || "", onChange: (e) => handleSetEndDate(e.target.value), className: "border-input bg-background text-foreground focus:ring-ring w-full rounded-md border px-3 py-2 focus:border-transparent focus:ring-2 focus:outline-none" })] }), _jsx(EnhancedRadio, { label: t("enabled"), name: "filterEnabled", value: filterEnabledValue, options: [
24
+ { label: t("all"), value: "undefined" },
25
+ { label: t("enabled"), value: "true" },
26
+ { label: t("disabled"), value: "false" },
27
+ ], onValueChange: (next) => {
28
+ const parsed = next === "true" ? true : next === "false" ? false : undefined;
29
+ handleChange("filterEnabled", parsed);
30
+ } })] }) }));
31
+ };
@@ -0,0 +1 @@
1
+ export declare const AdmissionForm: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,86 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * Admission Form Component
5
+ *
6
+ * Create/edit form for admission records. Uses the module state pattern:
7
+ * `const { state, handleChange, dispatch } = useAdmissionModule()`
8
+ *
9
+ * Note: the `state` field in `moduleState` (province/state) is aliased
10
+ * to `provinceState` to avoid a name collision with the hook's `state` object.
11
+ */
12
+ import { EnhancedInput } from "@appcorp/shadcn/components/enhanced-input";
13
+ import { EnhancedTextarea } from "@appcorp/shadcn/components/enhanced-textarea";
14
+ import { EnhancedCheckbox } from "@appcorp/shadcn/components/enhanced-checkbox";
15
+ import { EnhancedCombobox } from "@appcorp/shadcn/components/enhanced-combobox";
16
+ import { useAdmissionModule, ADMISSION_ACTION_TYPES } from "./context";
17
+ import { Separator } from "@appcorp/shadcn/components/ui/separator";
18
+ import { GENDER } from "../../type";
19
+ import { useEffect } from "react";
20
+ import { getCachedWorkspaceSync } from "../workspace/cache";
21
+ import { getDialCode, VALUE_DELIMITER } from "@react-pakistan/util-functions";
22
+ import { v4 as uuidv4 } from "uuid";
23
+ import { getCachedDiscountCodesSync } from "../discount-code/cache";
24
+ import { useTranslations } from "next-intl";
25
+ export const AdmissionForm = () => {
26
+ const { state, handleChange, dispatch } = useAdmissionModule();
27
+ const { address, admissionNotes, bForm, city, classForAdmission, country, discountCode, dob, emergencyContact, errors, fatherCnic, fatherFirstName, fatherLastName, fatherMobile, fatherOccupation, fatherOrganization, firstName, gender, id, lastName, motherCnic, motherFirstName, motherLastName, motherMobile, notes, previousSchool, registrationCode, siblings, state: provinceState, } = state;
28
+ const t = useTranslations("admission");
29
+ const workspace = getCachedWorkspaceSync();
30
+ const discountCodes = getCachedDiscountCodesSync();
31
+ const discountCodeOptions = (discountCodes.items || [])
32
+ .filter((code) => code.enabled)
33
+ .map((code) => {
34
+ var _a;
35
+ return ({
36
+ label: `${code.code} - ${code.discountValue}${code.discountType === "PERCENTAGE" ? "%" : (_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.currency}`,
37
+ value: code.code,
38
+ description: code.description || undefined,
39
+ });
40
+ });
41
+ useEffect(() => {
42
+ var _a, _b, _c, _d, _e, _f;
43
+ if (!id) {
44
+ dispatch({
45
+ type: ADMISSION_ACTION_TYPES.SET_INPUT_FIELD,
46
+ payload: { key: "city", value: ((_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.city) || "" },
47
+ });
48
+ dispatch({
49
+ type: ADMISSION_ACTION_TYPES.SET_INPUT_FIELD,
50
+ payload: { key: "state", value: ((_b = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _b === void 0 ? void 0 : _b.state) || "" },
51
+ });
52
+ dispatch({
53
+ type: ADMISSION_ACTION_TYPES.SET_INPUT_FIELD,
54
+ payload: { key: "country", value: ((_c = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _c === void 0 ? void 0 : _c.country) || "" },
55
+ });
56
+ dispatch({
57
+ type: ADMISSION_ACTION_TYPES.SET_INPUT_FIELD,
58
+ payload: {
59
+ key: "fatherMobile",
60
+ value: getDialCode(((_d = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _d === void 0 ? void 0 : _d.country) || "PK") || "",
61
+ },
62
+ });
63
+ dispatch({
64
+ type: ADMISSION_ACTION_TYPES.SET_INPUT_FIELD,
65
+ payload: {
66
+ key: "motherMobile",
67
+ value: getDialCode(((_e = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _e === void 0 ? void 0 : _e.country) || "PK") || "",
68
+ },
69
+ });
70
+ dispatch({
71
+ type: ADMISSION_ACTION_TYPES.SET_INPUT_FIELD,
72
+ payload: {
73
+ key: "registrationCode",
74
+ value: `${(_f = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _f === void 0 ? void 0 : _f.code}${VALUE_DELIMITER.COLON}${uuidv4()
75
+ .slice(0, 8)
76
+ .toUpperCase()}`,
77
+ },
78
+ });
79
+ }
80
+ }, [id]);
81
+ return (_jsxs("div", { className: "grid grid-cols-1 gap-2 space-y-2", children: [_jsx("h4", { className: "m-0 p-0", children: t("studentInformation") }), _jsxs("div", { className: "grid grid-cols-5 gap-4", children: [_jsx(EnhancedInput, { error: errors.firstName, id: "firstName", info: t("firstNameInfo"), label: t("firstName"), onChange: (e) => handleChange("firstName", e.target.value), placeholder: t("enterFirstName"), required: true, type: "text", value: firstName || "" }), _jsx(EnhancedInput, { error: errors.lastName, id: "lastName", info: t("lastNameInfo"), label: t("lastName"), onChange: (e) => handleChange("lastName", e.target.value), placeholder: t("enterLastName"), required: true, type: "text", value: lastName || "" }), _jsx(EnhancedInput, { error: errors.bForm, id: "bForm", info: t("bFormInfo"), label: t("bForm"), onChange: (e) => handleChange("bForm", e.target.value), placeholder: t("enterBForm"), required: true, type: "text", value: bForm || "" }), _jsx(EnhancedCombobox, { emptyText: t("noGenderOptionsFound"), error: errors.gender, id: "gender", info: t("genderInfo"), label: t("gender"), required: true, onValueChange: (value) => handleChange("gender", value), options: [
82
+ { label: t("male"), value: GENDER.MALE },
83
+ { label: t("female"), value: GENDER.FEMALE },
84
+ { label: t("other"), value: GENDER.OTHER },
85
+ ], placeholder: t("selectGender"), searchPlaceholder: t("searchGender"), value: gender }), _jsx(EnhancedInput, { error: errors.dob, id: "dob", info: t("dobInfo"), label: t("dob"), onChange: (e) => handleChange("dob", e.target.value), placeholder: t("selectDob"), required: true, type: "date", value: dob || "" }), _jsx(EnhancedInput, { error: errors.registrationCode, id: "registrationCode", info: t("registrationCodeInfo"), label: t("registrationCode"), onChange: (e) => handleChange("registrationCode", e.target.value), placeholder: t("enterRegistrationCode"), required: true, type: "text", value: registrationCode || "", disabled: true }), _jsx(EnhancedCombobox, { emptyText: t("noDiscountCodesAvailable"), error: errors.discountCode, id: "discountCode", info: t("discountCodeInfo"), label: t("discountCode"), onValueChange: (value) => handleChange("discountCode", value), options: discountCodeOptions, placeholder: t("enterDiscountCode"), searchPlaceholder: t("searchDiscountCodes"), value: discountCode || "" }), _jsx(EnhancedCheckbox, { label: t("hafiz"), id: "hafiz", info: t("hafizInfo"), onCheckedChange: (checked) => handleChange("hafiz", checked) }), _jsx(EnhancedCheckbox, { label: t("orphan"), id: "orphan", info: t("orphanInfo"), onCheckedChange: (checked) => handleChange("orphan", checked) })] }), _jsx(Separator, {}), _jsx("h4", { className: "m-0 p-0", children: t("fatherInformation") }), _jsxs("div", { className: "grid grid-cols-6 gap-4", children: [_jsx(EnhancedInput, { error: errors.fatherFirstName, id: "fatherFirstName", info: t("fatherFirstNameInfo"), label: t("firstName"), onChange: (e) => handleChange("fatherFirstName", e.target.value), placeholder: t("enterFirstName"), required: true, type: "text", value: fatherFirstName || "" }), _jsx(EnhancedInput, { error: errors.fatherLastName, id: "fatherLastName", info: t("fatherLastNameInfo"), label: t("lastName"), onChange: (e) => handleChange("fatherLastName", e.target.value), placeholder: t("enterLastName"), required: true, type: "text", value: fatherLastName || "" }), _jsx(EnhancedInput, { error: errors.fatherCnic, id: "fatherCnic", info: t("fatherCnicInfo"), label: t("cnic"), onChange: (e) => handleChange("fatherCnic", e.target.value), placeholder: t("enterBForm"), required: true, type: "text", value: fatherCnic || "" }), _jsx(EnhancedInput, { error: errors.fatherMobile, id: "fatherMobile", info: t("fatherMobileInfo"), label: t("mobile"), onChange: (e) => handleChange("fatherMobile", e.target.value), placeholder: "+923001234567", required: true, type: "text", value: fatherMobile || "" }), _jsx(EnhancedInput, { error: errors.fatherOccupation, id: "fatherOccupation", info: t("fatherOccupationInfo"), label: t("occupation"), onChange: (e) => handleChange("fatherOccupation", e.target.value), placeholder: t("enterOccupation"), required: false, type: "text", value: fatherOccupation || "" }), _jsx(EnhancedInput, { error: errors.fatherOrganization, id: "fatherOrganization", info: t("fatherOrganizationInfo"), label: t("organization"), onChange: (e) => handleChange("fatherOrganization", e.target.value), placeholder: t("enterOrganization"), required: false, type: "text", value: fatherOrganization || "" }), _jsx(EnhancedCheckbox, { label: t("emergencyContact"), id: "emergencyContactFather", info: t("fatherEmergencyContactInfo"), checked: emergencyContact === "Father", onCheckedChange: (checked) => handleChange("emergencyContact", checked ? "Father" : "") })] }), _jsx(Separator, {}), _jsx("h4", { className: "m-0 p-0", children: t("motherInformation") }), _jsxs("div", { className: "grid grid-cols-6 gap-4", children: [_jsx(EnhancedInput, { error: errors.motherFirstName, id: "motherFirstName", info: t("motherFirstNameInfo"), label: t("firstName"), onChange: (e) => handleChange("motherFirstName", e.target.value), placeholder: t("enterFirstName"), required: true, type: "text", value: motherFirstName || "" }), _jsx(EnhancedInput, { error: errors.motherLastName, id: "motherLastName", info: t("motherLastNameInfo"), label: t("lastName"), onChange: (e) => handleChange("motherLastName", e.target.value), placeholder: t("enterLastName"), required: true, type: "text", value: motherLastName || "" }), _jsx(EnhancedInput, { error: errors.motherCnic, id: "motherCnic", info: t("motherCnicInfo"), label: t("cnic"), onChange: (e) => handleChange("motherCnic", e.target.value), placeholder: t("enterBForm"), required: true, type: "text", value: motherCnic || "" }), _jsx(EnhancedInput, { error: errors.motherMobile, id: "motherMobile", info: t("motherMobileInfo"), label: t("mobile"), onChange: (e) => handleChange("motherMobile", e.target.value), placeholder: "+923001234567", required: true, type: "text", value: motherMobile || "" }), _jsx(EnhancedCheckbox, { label: t("emergencyContact"), id: "emergencyContactMother", info: t("motherEmergencyContactInfo"), checked: emergencyContact === "Mother", onCheckedChange: (checked) => handleChange("emergencyContact", checked ? "Mother" : "") })] }), _jsx(Separator, {}), _jsx("h4", { className: "m-0 p-0", children: t("homeInformation") }), _jsxs("div", { className: "grid grid-cols-6 gap-4", children: [_jsx(EnhancedTextarea, { error: errors.address, id: "homeAddress", info: t("homeAddressInfo"), label: t("homeAddress"), onChange: (e) => handleChange("address", e.target.value), placeholder: t("enterHomeAddress"), required: true, rows: 2, value: address || "" }), _jsx(EnhancedInput, { error: errors.city, id: "city", info: t("cityInfo"), label: t("city"), onChange: (e) => handleChange("city", e.target.value), placeholder: t("enterCity"), required: true, type: "text", value: city || "" }), _jsx(EnhancedInput, { error: errors.state, id: "state", info: t("stateInfo"), label: t("state"), onChange: (e) => handleChange("state", e.target.value), placeholder: t("enterState"), required: true, type: "text", value: provinceState || "" }), _jsx(EnhancedInput, { error: errors.country, id: "country", info: t("countryInfo"), label: t("country"), onChange: (e) => handleChange("country", e.target.value), placeholder: t("enterCountry"), required: true, type: "text", value: country || "" })] }), _jsx(Separator, {}), _jsx("h4", { className: "m-0 p-0", children: t("admissionInformation") }), _jsxs("div", { className: "grid grid-cols-6 gap-4", children: [_jsx(EnhancedInput, { error: errors.classForAdmission, id: "classForAdmission", info: t("classForAdmissionInfo"), label: t("classForAdmission"), onChange: (e) => handleChange("classForAdmission", e.target.value), placeholder: t("enterClassForAdmission"), required: true, type: "text", value: classForAdmission || "" }), _jsx(EnhancedInput, { error: errors.previousSchool, id: "previousSchool", info: t("previousSchoolInfo"), label: t("previousSchool"), onChange: (e) => handleChange("previousSchool", e.target.value), placeholder: t("enterPreviousSchool"), type: "text", value: previousSchool || "" }), _jsx(EnhancedTextarea, { error: errors.siblings, id: "siblings", info: t("siblingsInfo"), label: t("siblings"), onChange: (e) => handleChange("siblings", e.target.value), placeholder: t("enterSiblings"), rows: 3, value: siblings || "" })] }), _jsx(Separator, {}), _jsx("h4", { className: "m-0 p-0", children: t("officeUse") }), _jsxs("div", { className: "grid grid-cols-4 gap-4", children: [_jsx(EnhancedTextarea, { error: errors.notes, id: "notes", info: t("notesInfo"), label: t("notes"), onChange: (e) => handleChange("notes", e.target.value), placeholder: t("enterNotes"), rows: 3, value: notes || "" }), _jsx(EnhancedTextarea, { error: errors.admissionNotes, id: "admissionNotes", info: t("admissionNotesInfo"), label: t("admissionNotes"), onChange: (e) => handleChange("admissionNotes", e.target.value), placeholder: t("enterAdmissionNotes"), rows: 3, value: admissionNotes || "" })] })] }));
86
+ };
@@ -0,0 +1 @@
1
+ export declare const AdmissionMoreActions: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,5 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ export const AdmissionMoreActions = () => {
4
+ return _jsx("div", { className: "space-y-4", children: "More actions for admissions" });
5
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Admission Page Component
3
+ *
4
+ * Thin wrapper around GenericModulePage. All handlers, header actions, and row
5
+ * actions live in context.tsx — this file only owns:
6
+ * - tableBodyCols (static, module-level constant)
7
+ * - admissionConfig (memoised on locale change only; size fixed to "full" so
8
+ * the component type produced by createGenericModulePage never changes at
9
+ * runtime, preventing the infinite-remount re-render loop)
10
+ * - permission guard
11
+ */
12
+ import { FC } from "react";
13
+ import { USER_ROLE } from "../../type";
14
+ export declare const AdmissionPage: FC<{
15
+ userRole: USER_ROLE;
16
+ }>;
@@ -0,0 +1,128 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ /**
4
+ * Admission Page Component
5
+ *
6
+ * Thin wrapper around GenericModulePage. All handlers, header actions, and row
7
+ * actions live in context.tsx — this file only owns:
8
+ * - tableBodyCols (static, module-level constant)
9
+ * - admissionConfig (memoised on locale change only; size fixed to "full" so
10
+ * the component type produced by createGenericModulePage never changes at
11
+ * runtime, preventing the infinite-remount re-render loop)
12
+ * - permission guard
13
+ */
14
+ import { useMemo } from "react";
15
+ import { COMPONENT_TYPE } from "@appcorp/shadcn/components/enhanced-table";
16
+ import { createGenericModulePage, } from "@react-pakistan/util-functions/factory/generic-component-factory";
17
+ import { useAdmissionModule, AdmissionProvider, ADMISSION_DRAWER, ADMISSION_ACTION_TYPES, } from "./context";
18
+ import { AdmissionFilter } from "./filter";
19
+ import { AdmissionForm } from "./form";
20
+ import { AdmissionView } from "./view";
21
+ import { resolveRbacPermissions } from "../../utils/resolve-rbac-permissions";
22
+ import { RbacNoAccess } from "../../components/rbac-no-access";
23
+ import { useTranslations } from "next-intl";
24
+ import { AdmissionMoreActions } from "./more-actions";
25
+ // ============================================================================
26
+ // TABLE COLUMN CONFIGURATION (static — no runtime deps)
27
+ // ============================================================================
28
+ const tableBodyCols = [
29
+ { componentType: COMPONENT_TYPE.ID, key: "id" },
30
+ { componentType: COMPONENT_TYPE.OBJECT, key: ["studentDetails:firstName"] },
31
+ { componentType: COMPONENT_TYPE.OBJECT, key: ["studentDetails:lastName"] },
32
+ {
33
+ componentType: COMPONENT_TYPE.OBJECT,
34
+ key: ["studentDetails:registrationCode"],
35
+ },
36
+ {
37
+ componentType: COMPONENT_TYPE.OBJECT,
38
+ key: ["admissionDetails:classForAdmission"],
39
+ },
40
+ { componentType: COMPONENT_TYPE.TEXT, key: "status" },
41
+ { componentType: COMPONENT_TYPE.BOOLEAN, key: "enabled" },
42
+ { componentType: COMPONENT_TYPE.ACTIONS },
43
+ ];
44
+ const tableColumns = [
45
+ { label: "id", width: "5%" },
46
+ { label: "firstName", width: "15%" },
47
+ { label: "lastName", width: "15%" },
48
+ { label: "registrationCode", width: "20%" },
49
+ { label: "class", width: "20%" },
50
+ { label: "status", width: "10%" },
51
+ { label: "enabled", width: "10%" },
52
+ { label: "actions", width: "5%" },
53
+ ];
54
+ // ============================================================================
55
+ // TRANSLATED COLUMNS HELPER
56
+ // ============================================================================
57
+ const getTranslatedColumns = (t) => tableColumns.map((col) => (Object.assign(Object.assign({}, col), { label: t(col.label) })));
58
+ // ============================================================================
59
+ // COMPONENT FACTORY (creates JSX elements when config is created, not during render)
60
+ // ============================================================================
61
+ const createComponentInstances = () => ({
62
+ filter: _jsx(AdmissionFilter, {}),
63
+ form: _jsx(AdmissionForm, {}),
64
+ moreActions: _jsx(AdmissionMoreActions, {}),
65
+ view: _jsx(AdmissionView, {}),
66
+ });
67
+ // ============================================================================
68
+ // CONFIG CREATION HELPER
69
+ // ============================================================================
70
+ const createAdmissionConfig = (t, drawer, dispatch) => {
71
+ const components = createComponentInstances();
72
+ return {
73
+ moduleName: "admission",
74
+ tableColumns: getTranslatedColumns(t),
75
+ cancelLabel: t("cancel"),
76
+ drawerTitle: t("admission"),
77
+ filterContent: components.filter,
78
+ formContent: components.form,
79
+ moreActionsContent: components.moreActions,
80
+ saveLabel: t("save"),
81
+ searchPlaceholder: t("searchAdmissions"),
82
+ tableDescription: t("manageAdmissionsInTheSystem"),
83
+ tableTitle: t("admission"),
84
+ viewContent: components.view,
85
+ size: drawer === ADMISSION_DRAWER.FORM_DRAWER ? "full" : "small",
86
+ onClearFilters: () => {
87
+ dispatch({ type: ADMISSION_ACTION_TYPES.RESET_FORM });
88
+ },
89
+ };
90
+ };
91
+ // ============================================================================
92
+ // STABLE PAGE COMPONENT (created once, outside render)
93
+ // ============================================================================
94
+ const GenericAdmissionPage = createGenericModulePage({
95
+ moduleName: "admission",
96
+ tableColumns: [],
97
+ cancelLabel: "",
98
+ drawerTitle: "",
99
+ filterContent: _jsx(AdmissionFilter, {}),
100
+ formContent: _jsx(AdmissionForm, {}),
101
+ moreActionsContent: _jsx(AdmissionMoreActions, {}),
102
+ saveLabel: "",
103
+ searchPlaceholder: "",
104
+ tableDescription: "",
105
+ tableTitle: "",
106
+ viewContent: _jsx(AdmissionView, {}),
107
+ size: "small",
108
+ onClearFilters: () => { },
109
+ });
110
+ // ============================================================================
111
+ // INNER PAGE (requires AdmissionProvider context)
112
+ // ============================================================================
113
+ const AdmissionPageInner = ({ userRole }) => {
114
+ const t = useTranslations("admission");
115
+ const context = useAdmissionModule();
116
+ // Memoize permission check to avoid recalculation on every render
117
+ const hasPermission = useMemo(() => resolveRbacPermissions({
118
+ userRole,
119
+ moduleName: "Admission",
120
+ }), [userRole]);
121
+ // Memoize config creation
122
+ const admissionConfig = useMemo(() => createAdmissionConfig(t, context.state.drawer, context.dispatch), [t, context.state.drawer, context.dispatch]);
123
+ if (!hasPermission) {
124
+ return _jsx(RbacNoAccess, { moduleName: "Admission" });
125
+ }
126
+ return (_jsx("div", { className: "p-4", children: _jsx(GenericAdmissionPage, { config: admissionConfig, context: context, tableBodyCols: tableBodyCols }) }));
127
+ };
128
+ export const AdmissionPage = ({ userRole }) => (_jsx(AdmissionProvider, { children: _jsx(AdmissionPageInner, { userRole: userRole }) }));
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Admission Validation Schema
3
+ *
4
+ * Zod validation schema for admission form with i18n keys
5
+ */
6
+ import { z } from "zod";
7
+ import { GENDER } from "../../type";
8
+ export declare const admissionFormValidation: z.ZodObject<{
9
+ registrationCode: z.ZodString;
10
+ firstName: z.ZodString;
11
+ lastName: z.ZodString;
12
+ bForm: z.ZodString;
13
+ dob: z.ZodString;
14
+ gender: z.ZodEnum<typeof GENDER>;
15
+ discountCode: z.ZodOptional<z.ZodString>;
16
+ hafiz: z.ZodOptional<z.ZodBoolean>;
17
+ orphan: z.ZodOptional<z.ZodBoolean>;
18
+ fatherFirstName: z.ZodString;
19
+ fatherLastName: z.ZodString;
20
+ fatherCnic: z.ZodString;
21
+ fatherMobile: z.ZodString;
22
+ fatherOccupation: z.ZodOptional<z.ZodString>;
23
+ fatherOrganization: z.ZodOptional<z.ZodString>;
24
+ motherFirstName: z.ZodString;
25
+ motherLastName: z.ZodString;
26
+ motherCnic: z.ZodString;
27
+ motherMobile: z.ZodString;
28
+ address: z.ZodString;
29
+ city: z.ZodString;
30
+ state: z.ZodString;
31
+ country: z.ZodString;
32
+ classForAdmission: z.ZodString;
33
+ previousSchool: z.ZodOptional<z.ZodString>;
34
+ siblings: z.ZodOptional<z.ZodString>;
35
+ notes: z.ZodOptional<z.ZodString>;
36
+ admissionNotes: z.ZodOptional<z.ZodString>;
37
+ emergencyContact: z.ZodOptional<z.ZodString>;
38
+ }, z.core.$strip>;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Admission Validation Schema
3
+ *
4
+ * Zod validation schema for admission form with i18n keys
5
+ */
6
+ import { z } from "zod";
7
+ import { GENDER } from "../../type";
8
+ export const admissionFormValidation = z.object({
9
+ // Student Information
10
+ registrationCode: z.string().min(1, "Registration code is required"),
11
+ firstName: z.string().min(1, "First name is required"),
12
+ lastName: z.string().min(1, "Last name is required"),
13
+ bForm: z
14
+ .string()
15
+ .min(1, "B. Form is required")
16
+ .max(15, "B. Form must be 15 characters")
17
+ .regex(/^\d{5}-\d{7}-\d{1}$/, "B. Form must be in format XXXXX-XXXXXXX-X"),
18
+ dob: z
19
+ .string()
20
+ .min(1, "Date of birth is required")
21
+ .refine((dateStr) => {
22
+ const birthDate = new Date(dateStr);
23
+ const today = new Date();
24
+ const age = today.getFullYear() - birthDate.getFullYear();
25
+ // Adjust age if birthday hasn't occurred this year
26
+ const adjustedAge = today.getMonth() < birthDate.getMonth() ||
27
+ (today.getMonth() === birthDate.getMonth() &&
28
+ today.getDate() < birthDate.getDate())
29
+ ? age - 1
30
+ : age;
31
+ return adjustedAge >= 2 && adjustedAge <= 25;
32
+ }, "Student age must be between 2 and 25 years"),
33
+ gender: z.nativeEnum(GENDER, { error: "Gender is required" }),
34
+ discountCode: z.string().optional(),
35
+ hafiz: z.boolean().optional(),
36
+ orphan: z.boolean().optional(),
37
+ // Father Information
38
+ fatherFirstName: z.string().min(1, "Father's first name is required"),
39
+ fatherLastName: z.string().min(1, "Father's last name is required"),
40
+ fatherCnic: z
41
+ .string()
42
+ .min(1, "Father's CNIC is required")
43
+ .max(15, "Father's CNIC must be 15 characters")
44
+ .regex(/^\d{5}-\d{7}-\d{1}$/, "Father's CNIC must be in format XXXXX-XXXXXXX-X"),
45
+ fatherMobile: z.string().min(1, "Father's mobile number is required"),
46
+ fatherOccupation: z.string().optional(),
47
+ fatherOrganization: z.string().optional(),
48
+ // Mother Information
49
+ motherFirstName: z.string().min(1, "Mother's first name is required"),
50
+ motherLastName: z.string().min(1, "Mother's last name is required"),
51
+ motherCnic: z
52
+ .string()
53
+ .min(1, "Mother's CNIC is required")
54
+ .max(15, "Mother's CNIC must be 15 characters")
55
+ .regex(/^\d{5}-\d{7}-\d{1}$/, "Mother's CNIC must be in format XXXXX-XXXXXXX-X"),
56
+ motherMobile: z.string().min(1, "Mother's mobile number is required"),
57
+ // Home Information
58
+ address: z.string().min(1, "Home address is required"),
59
+ city: z.string().min(1, "City is required"),
60
+ state: z.string().min(1, "State is required"),
61
+ country: z.string().min(1, "Country is required"),
62
+ // Admission Information
63
+ classForAdmission: z.string().min(1, "Class for admission is required"),
64
+ previousSchool: z.string().optional(),
65
+ siblings: z.string().optional(),
66
+ // Office Use
67
+ notes: z.string().optional(),
68
+ admissionNotes: z.string().optional(),
69
+ emergencyContact: z.string().optional(),
70
+ });
@@ -0,0 +1 @@
1
+ export declare const AdmissionView: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,40 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * Admission View Component
5
+ *
6
+ * Read-only details view for an admission record. Uses the module state pattern:
7
+ * `const { state, handleAnalyze } = useAdmissionModule()`
8
+ *
9
+ * Note: the `state` field in `moduleState` (province/state) is aliased
10
+ * to `provinceState` to avoid a name collision with the hook's `state` object.
11
+ */
12
+ import { useAdmissionModule } from "./context";
13
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@appcorp/shadcn/components/ui/card";
14
+ import { Badge } from "@appcorp/shadcn/components/ui/badge";
15
+ import { Separator } from "@appcorp/shadcn/components/ui/separator";
16
+ import { AlertTriangle, BookOpen, Brain, Calendar, CheckCircle2, FileText, Home, Loader2, RefreshCw, ShieldCheck, ShieldX, User, XCircle, } from "lucide-react";
17
+ import { Button } from "@appcorp/shadcn/components/ui/button";
18
+ import { useTranslations } from "next-intl";
19
+ import { formatValue } from "../../utils/format-value";
20
+ import { formatDate, DATE_FORMATS } from "@react-pakistan/util-functions";
21
+ export const AdmissionView = () => {
22
+ const { state, handleAnalyze } = useAdmissionModule();
23
+ const { address, admissionNotes, aiAnalysis, analyzeLoading, analyzeError, bForm, city, classForAdmission, country, discountCode, dob, emergencyContact, enabled, fatherCnic, fatherFirstName, fatherLastName, fatherMobile, fatherOccupation, fatherOrganization, firstName, gender, hafiz, id, lastName, motherCnic, motherFirstName, motherLastName, motherMobile, notes, orphan, postalCode, previousSchool, registrationCode, siblings, state: provinceState, status, } = state;
24
+ const t = useTranslations("admission");
25
+ const decisionColor = (decision) => {
26
+ if (decision === "APPROVED")
27
+ return "bg-green-100 text-green-800 border-green-200";
28
+ if (decision === "REJECTED")
29
+ return "bg-red-100 text-red-800 border-red-200";
30
+ return "bg-yellow-100 text-yellow-800 border-yellow-200";
31
+ };
32
+ const decisionIcon = (decision) => {
33
+ if (decision === "APPROVED")
34
+ return _jsx(ShieldCheck, { className: "h-4 w-4" });
35
+ if (decision === "REJECTED")
36
+ return _jsx(ShieldX, { className: "h-4 w-4" });
37
+ return _jsx(AlertTriangle, { className: "h-4 w-4" });
38
+ };
39
+ return (_jsxs("div", { className: "space-y-6", children: [_jsxs(Card, { className: "border-primary/20 border-2", children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Brain, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: t("aiAnalysis") })] }), _jsxs(Button, { variant: "outline", size: "sm", disabled: analyzeLoading, onClick: () => handleAnalyze({ id }), children: [analyzeLoading ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(RefreshCw, { className: "mr-2 h-4 w-4" })), aiAnalysis ? t("reRunAnalysis") : t("analyzeWithAi")] })] }), _jsx(CardDescription, { children: t("geminiPoweredAdmissionScoring") })] }), _jsx(Separator, {}), _jsxs(CardContent, { className: "pt-6", children: [analyzeLoading && (_jsxs("div", { className: "text-muted-foreground flex items-center gap-3", children: [_jsx(Loader2, { className: "h-5 w-5 animate-spin" }), _jsx("span", { children: t("runningAiAnalysis") })] })), analyzeError && !analyzeLoading && (_jsxs("div", { className: "text-destructive flex items-center gap-2 text-sm", children: [_jsx(AlertTriangle, { className: "h-4 w-4" }), _jsx("span", { children: analyzeError })] })), !analyzeLoading && aiAnalysis && (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-4", children: [_jsxs("div", { className: "bg-muted flex flex-col items-center rounded-lg px-6 py-3", children: [_jsx("span", { className: "text-primary text-3xl font-bold", children: aiAnalysis.score }), _jsx("span", { className: "text-muted-foreground text-xs tracking-wide uppercase", children: "/ 100" })] }), _jsxs("span", { className: `inline-flex items-center gap-1.5 rounded-full border px-3 py-1.5 text-sm font-medium ${decisionColor(aiAnalysis.decision)}`, children: [decisionIcon(aiAnalysis.decision), aiAnalysis.decision.replace("_", " ")] }), _jsxs("div", { className: "text-muted-foreground text-sm", children: [_jsxs("span", { className: "font-medium", children: [t("confidence"), ": "] }), (aiAnalysis.confidence * 100).toFixed(0), "%"] })] }), aiAnalysis.reasons.length > 0 && (_jsxs("div", { children: [_jsx("p", { className: "mb-2 text-sm font-medium", children: t("aiReasoningAndRiskFlags") }), _jsx("ul", { className: "space-y-1.5", children: aiAnalysis.reasons.map((reason, i) => (_jsxs("li", { className: "flex items-start gap-2 text-sm", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-yellow-500" }), _jsx("span", { children: reason })] }, i))) })] })), _jsxs("p", { className: "text-muted-foreground text-xs", children: [t("lastAnalyzed"), ":", " ", formatDate(String(aiAnalysis.processedAt), DATE_FORMATS.LOCALE_DATE)] })] })), !analyzeLoading && !aiAnalysis && !analyzeError && (_jsx("p", { className: "text-muted-foreground text-sm", children: t("noAiAnalysisYet") }))] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(BookOpen, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: t("admissionDetails") })] }), _jsx(CardDescription, { children: t("registrationAndAdmissionInformation") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("registrationCode") }), _jsx("p", { className: "font-mono text-base", children: formatValue(registrationCode) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("classForAdmission") }), _jsx("p", { className: "text-base", children: formatValue(classForAdmission) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("status") }), _jsx("p", { className: "text-base", children: formatValue(status) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("previousSchool") }), _jsx("p", { className: "text-base", children: formatValue(previousSchool) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("siblings") }), _jsx("p", { className: "text-base", children: formatValue(siblings) })] })] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(User, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: t("studentInformation") })] }), _jsx(CardDescription, { children: t("personalDetails") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("fullName") }), _jsxs("p", { className: "text-base", children: [formatValue(firstName), " ", formatValue(lastName)] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("dob") }), _jsx("p", { className: "text-base", children: formatDate(String(dob), DATE_FORMATS.LOCALE_DATE) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("gender") }), _jsx("p", { className: "text-base", children: formatValue(gender) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("bForm") }), _jsx("p", { className: "text-base", children: formatValue(bForm) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("discountCode") }), _jsx("p", { className: "text-base", children: formatValue(discountCode) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("emergencyContact") }), _jsx("p", { className: "text-base", children: formatValue(emergencyContact) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("hafizEQuran") }), _jsx("p", { className: "text-base", children: hafiz ? t("yes") : t("no") })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("orphan") }), _jsx("p", { className: "text-base", children: orphan ? t("yes") : t("no") })] })] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(User, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: t("fatherInformation") })] }), _jsx(CardDescription, { children: t("fatherDetails") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("fullName") }), _jsxs("p", { className: "text-base", children: [formatValue(fatherFirstName), " ", formatValue(fatherLastName)] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("cnic") }), _jsx("p", { className: "text-base", children: formatValue(fatherCnic) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("mobile") }), _jsx("p", { className: "text-base", children: formatValue(fatherMobile) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("occupation") }), _jsx("p", { className: "text-base", children: formatValue(fatherOccupation) })] }), _jsxs("div", { className: "space-y-1 md:col-span-2", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("organization") }), _jsx("p", { className: "text-base", children: formatValue(fatherOrganization) })] })] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(User, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: t("motherInformation") })] }), _jsx(CardDescription, { children: t("motherDetails") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("fullName") }), _jsxs("p", { className: "text-base", children: [formatValue(motherFirstName), " ", formatValue(motherLastName)] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("cnic") }), _jsx("p", { className: "text-base", children: formatValue(motherCnic) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("mobile") }), _jsx("p", { className: "text-base", children: formatValue(motherMobile) })] })] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Home, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: t("homeDetails") })] }), _jsx(CardDescription, { children: t("addressInformation") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-1 md:col-span-2", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("address") }), _jsx("p", { className: "text-base", children: formatValue(address) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("city") }), _jsx("p", { className: "text-base", children: formatValue(city) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("state") }), _jsx("p", { className: "text-base", children: formatValue(provinceState) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("country") }), _jsx("p", { className: "text-base", children: formatValue(country) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("postalCode") }), _jsx("p", { className: "text-base", children: formatValue(postalCode) })] })] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(FileText, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: t("officeUse") })] }), _jsx(CardDescription, { children: t("internalNotesAndProcessingInformation") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-1 gap-6", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("notes") }), _jsx("p", { className: "text-base", children: formatValue(notes) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("admissionNotes") }), _jsx("p", { className: "text-base", children: formatValue(admissionNotes) })] })] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Calendar, { className: "text-primary h-5 w-5" }), _jsx(CardTitle, { className: "text-lg", children: t("recordStatus") })] }), _jsx(CardDescription, { children: t("recordStateAndMetadata") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-sm font-medium", children: t("enabled") }), _jsxs(Badge, { variant: enabled ? "default" : "destructive", className: "gap-1", children: [enabled ? (_jsx(CheckCircle2, { className: "h-3 w-3" })) : (_jsx(XCircle, { className: "h-3 w-3" })), enabled ? t("active") : t("inactive")] })] }) })] })] }));
40
+ };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Discount Code Cache utilities
3
+ *
4
+ * Convenience wrappers around the shared cache helpers used across modules.
5
+ * These helpers are intentionally small: callers in the UI layer expect
6
+ * simple, well-typed functions such as `getCachedDiscountCodes` and
7
+ * `invalidateDiscountCodeCache`.
8
+ *
9
+ * Usage examples:
10
+ * - `await getCachedDiscountCodes({ params: { page: 1 } })`
11
+ * - `invalidateDiscountCodeCache()` after a successful create/update/delete
12
+ */
13
+ import { DiscountCodeBE } from "@/type";
14
+ /** Synchronous access to cached discount codes (localStorage) */
15
+ export declare const getCachedDiscountCodesSync: () => import("@react-pakistan/util-functions").ListResponse<DiscountCodeBE>;
16
+ /** Fetch cached list (may call network if cache miss or stale) */
17
+ export declare const getCachedDiscountCodes: ({ params, }: {
18
+ params: Record<string, unknown>;
19
+ }) => Promise<import("@react-pakistan/util-functions").ListResponse<DiscountCodeBE>>;
20
+ /** Get a single discount code from the cache by id */
21
+ export declare const getCachedDiscountCodeById: (discountCodeId: string) => DiscountCodeBE | null;
22
+ /** Remove discount-code cache entries (used after writes) */
23
+ export declare const invalidateDiscountCodeCache: () => void;
24
+ /** Preload discount codes into cache (useful during bootstrap) */
25
+ export declare const preloadDiscountCodes: () => Promise<import("@react-pakistan/util-functions").ListResponse<DiscountCodeBE>>;
26
+ /** Utility to check if the discount codes cache is stale */
27
+ export declare const isDiscountCodesCacheStale: () => boolean;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Discount Code Cache utilities
3
+ *
4
+ * Convenience wrappers around the shared cache helpers used across modules.
5
+ * These helpers are intentionally small: callers in the UI layer expect
6
+ * simple, well-typed functions such as `getCachedDiscountCodes` and
7
+ * `invalidateDiscountCodeCache`.
8
+ *
9
+ * Usage examples:
10
+ * - `await getCachedDiscountCodes({ params: { page: 1 } })`
11
+ * - `invalidateDiscountCodeCache()` after a successful create/update/delete
12
+ */
13
+ import { LS_KEYS } from "@/constants";
14
+ import { DISCOUNT_CODE_API_ROUTES } from "./constants";
15
+ import { getCachedData, getCachedDataSync, getCachedItemById, invalidateCache, isCacheStale, preloadCache, } from "@react-pakistan/util-functions";
16
+ // ============================================================================
17
+ // CACHE CONFIGURATION
18
+ // ============================================================================
19
+ const DISCOUNT_CODE_CACHE_CONFIG = {
20
+ cacheKey: LS_KEYS.DISCOUNT_CODES,
21
+ apiUrl: DISCOUNT_CODE_API_ROUTES.UNIT,
22
+ };
23
+ // ============================================================================
24
+ // DISCOUNT CODE CACHE FUNCTIONS
25
+ // ============================================================================
26
+ /** Synchronous access to cached discount codes (localStorage) */
27
+ export const getCachedDiscountCodesSync = () => getCachedDataSync(LS_KEYS.DISCOUNT_CODES);
28
+ /** Fetch cached list (may call network if cache miss or stale) */
29
+ export const getCachedDiscountCodes = ({ params, }) => getCachedData({
30
+ config: DISCOUNT_CODE_CACHE_CONFIG,
31
+ params,
32
+ headers: {
33
+ "Content-Type": "application/json",
34
+ "x-api-token": process.env.NEXT_PUBLIC_API_KEY,
35
+ },
36
+ });
37
+ /** Get a single discount code from the cache by id */
38
+ export const getCachedDiscountCodeById = (discountCodeId) => getCachedItemById(LS_KEYS.DISCOUNT_CODES, discountCodeId);
39
+ /** Remove discount-code cache entries (used after writes) */
40
+ export const invalidateDiscountCodeCache = () => {
41
+ invalidateCache(LS_KEYS.DISCOUNT_CODES);
42
+ };
43
+ /** Preload discount codes into cache (useful during bootstrap) */
44
+ export const preloadDiscountCodes = () => preloadCache(DISCOUNT_CODE_CACHE_CONFIG);
45
+ /** Utility to check if the discount codes cache is stale */
46
+ export const isDiscountCodesCacheStale = () => isCacheStale(LS_KEYS.DISCOUNT_CODES);
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Discount Code Constants
3
+ *
4
+ * Centralized constants used by the discount-code module. Keep values
5
+ * environment-driven (via `NEXT_PUBLIC_*`) where appropriate so CI/infra
6
+ * can override them without code changes.
7
+ */
8
+ /**
9
+ * Default page limit for the discount codes list. Controlled by
10
+ * `NEXT_PUBLIC_PAGE_LIMIT` with a safe fallback to `10`.
11
+ */
12
+ export declare const pageLimit: number;
13
+ /**
14
+ * API routes for discount-code operations. The backend expects requests to
15
+ * these endpoints and returns the standard `{ items, count }` list shape.
16
+ */
17
+ export declare const DISCOUNT_CODE_API_ROUTES: {
18
+ UNIT: string;
19
+ };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Discount Code Constants
3
+ *
4
+ * Centralized constants used by the discount-code module. Keep values
5
+ * environment-driven (via `NEXT_PUBLIC_*`) where appropriate so CI/infra
6
+ * can override them without code changes.
7
+ */
8
+ "use client";
9
+ // ============================================================================
10
+ // PAGE CONFIGURATION
11
+ // ============================================================================
12
+ /**
13
+ * Default page limit for the discount codes list. Controlled by
14
+ * `NEXT_PUBLIC_PAGE_LIMIT` with a safe fallback to `10`.
15
+ */
16
+ export const pageLimit = Number(process.env.NEXT_PUBLIC_PAGE_LIMIT) || 10;
17
+ // ============================================================================
18
+ // API ROUTES
19
+ // ============================================================================
20
+ /**
21
+ * API routes for discount-code operations. The backend expects requests to
22
+ * these endpoints and returns the standard `{ items, count }` list shape.
23
+ */
24
+ export const DISCOUNT_CODE_API_ROUTES = {
25
+ UNIT: "/api/v1/discount-code",
26
+ };