@appcorp/fusion-storybook 0.3.90 → 0.3.91

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.
@@ -0,0 +1,47 @@
1
+ "use client";
2
+ import { createGenericModule } from "@react-pakistan/util-functions/factory/generic-module-factory";
3
+ import { DRAWER_TYPES } from "@react-pakistan/util-functions/factory/generic-component-factory";
4
+ import { FEE_TYPE } from "../../../type";
5
+ import { pageLimit } from "../constants";
6
+ // ============================================================================
7
+ // 1.1 DRAWER TYPES
8
+ // ============================================================================
9
+ export const FEE_STRUCTURE_DRAWER = {
10
+ FILTER_DRAWER: DRAWER_TYPES.FILTER_DRAWER,
11
+ FORM_DRAWER: DRAWER_TYPES.FORM_DRAWER,
12
+ MORE_ACTIONS_DRAWER: DRAWER_TYPES.MORE_ACTIONS_DRAWER,
13
+ VIEW_DRAWER: DRAWER_TYPES.VIEW_DRAWER,
14
+ };
15
+ const feeStructureConfig = {
16
+ name: "FeeStructure",
17
+ displayName: "Fee Structure",
18
+ drawerTypes: DRAWER_TYPES,
19
+ initialState: {
20
+ // List Data
21
+ items: [],
22
+ count: 0,
23
+ // Search & Pagination
24
+ currentPage: 1,
25
+ pageLimit,
26
+ searchQuery: "",
27
+ // UI State
28
+ disableSaveButton: false,
29
+ drawer: null,
30
+ // Form State
31
+ amount: 0,
32
+ classId: null,
33
+ description: null,
34
+ enabled: true,
35
+ errors: {},
36
+ feeType: FEE_TYPE.TUITION,
37
+ filterEnabled: undefined,
38
+ frequency: null,
39
+ id: "",
40
+ name: "",
41
+ schoolId: "",
42
+ },
43
+ };
44
+ // ============================================================================
45
+ // 1.3 CREATE FEE STRUCTURE MODULE
46
+ // ============================================================================
47
+ export const { actionTypes: FEE_STRUCTURE_ACTION_TYPES, config: feeStructureModuleConfig, initialState: initialFeeStructureState, Provider: FeeStructureProvider, reducer: feeStructureReducer, useContext: useFeeStructureContext, } = createGenericModule(feeStructureConfig);
@@ -0,0 +1,54 @@
1
+ import { type RowAction, type TableRow } from "@appcorp/shadcn/components/enhanced-table";
2
+ import { FEE_TYPE } from "../../../type";
3
+ export declare const useFeeStructureModule: () => {
4
+ applyFilters: () => void;
5
+ byIdLoading: boolean;
6
+ clearFilters: () => void;
7
+ clearSearch: () => void;
8
+ closeDrawer: () => void;
9
+ deleteLoading: boolean;
10
+ handleChange: (field: string, value: string | number | boolean | FEE_TYPE | null | undefined) => void;
11
+ handleCreate: () => void;
12
+ handleDelete: (row?: TableRow) => void;
13
+ handleEdit: (row?: TableRow) => void;
14
+ handleFilters: () => void;
15
+ handleMoreActions: () => void;
16
+ handlePageChange: (page: number | unknown) => void;
17
+ handlePageLimitChange: (k: string, value: object) => void;
18
+ handleSearch: (query: string) => void;
19
+ handleSubmit: () => void;
20
+ handleView: (row?: TableRow) => void;
21
+ headerActions: {
22
+ enabled: boolean;
23
+ handleOnClick: () => void;
24
+ label: string;
25
+ order: number;
26
+ }[];
27
+ listFetchNow: (url?: string, config?: import("@react-pakistan/util-functions/hooks/use-fetch").FetchConfig) => void;
28
+ listLoading: boolean;
29
+ rowActions: RowAction[];
30
+ toggleStatus: (row?: TableRow) => void;
31
+ updateLoading: boolean;
32
+ handleCloseDrawer?: () => void;
33
+ state: {
34
+ items: import("../../../types").FeeStructureBE[];
35
+ count: number;
36
+ currentPage: number;
37
+ pageLimit: number;
38
+ searchQuery: string;
39
+ disableSaveButton: boolean;
40
+ drawer: string | null;
41
+ amount: number;
42
+ classId: string | null;
43
+ description: string | null;
44
+ enabled: boolean;
45
+ errors: Record<string, string>;
46
+ feeType: FEE_TYPE;
47
+ filterEnabled: boolean | undefined;
48
+ frequency: string | null;
49
+ id: string;
50
+ name: string;
51
+ schoolId: string;
52
+ };
53
+ dispatch: React.Dispatch<any>;
54
+ };
@@ -1,112 +1,31 @@
1
1
  "use client";
2
- /**
3
- * FeeStructure Module — business logic + API integration
4
- *
5
- * This module wires the generic module state (created by
6
- * `createGenericModule`) to network actions using `useModuleEntityV2`.
7
- * It contains the domain-specific handlers (create/view/edit/delete),
8
- * local validation wiring, and optimistic UI helpers.
9
- *
10
- * Key responsibilities:
11
- * - expose `useFeeStructureModule()` which UI components call for actions
12
- * - keep the module-specific `apiParams` and callbacks in one place
13
- * - ensure cache invalidation and toast notifications on mutation
14
- *
15
- * Exported utilities:
16
- * - `FeeStructureProvider` — provider component used by the page
17
- * - `useFeeStructureModule()` — hook that returns handlers and state
18
- */
19
- import { useCallback, useEffect, useMemo, useRef } from "react";
2
+ import { useCallback, useMemo } from "react";
20
3
  import { useTheme } from "next-themes";
21
4
  import { useTranslations } from "next-intl";
22
- import { isCreatedOrUpdated } from "@react-pakistan/util-functions/general/is-created-or-updated";
23
5
  import { validateForm } from "@react-pakistan/util-functions/general/validate-form";
24
- import { useModuleEntityV2, } from "@react-pakistan/util-functions/hooks/use-module-entity-v2";
25
- import { useDebounce } from "@react-pakistan/util-functions/hooks/use-debounce";
26
- import { createGenericModule } from "@react-pakistan/util-functions/factory/generic-module-factory";
27
- import { DRAWER_TYPES } from "@react-pakistan/util-functions/factory/generic-component-factory";
28
- import { pageLimit } from "./constants";
29
- import { FEE_STRUCTURE_API_ROUTES } from "../../constants";
30
- import { feeStructureFormValidation } from "./validate";
31
6
  import { generateThemeToast, TOAST_VARIANT, } from "@appcorp/shadcn/lib/toast-utils";
32
- import { FEE_TYPE } from "../../type";
33
- import { getCachedWorkspaceSync } from "../workspace/cache";
7
+ import { getCachedWorkspaceSync } from "../../workspace/cache";
8
+ import { feeStructureFormValidation } from "../validate";
9
+ import { FEE_STRUCTURE_ACTION_TYPES, FEE_STRUCTURE_DRAWER, useFeeStructureContext, } from "./shared";
10
+ import { useFeeStructureApiContext } from "./fee-structure-api-provider";
34
11
  // ============================================================================
35
- // 1.1 DRAWER TYPES
36
- // ============================================================================
37
- export const FEE_STRUCTURE_DRAWER = {
38
- FILTER_DRAWER: DRAWER_TYPES.FILTER_DRAWER,
39
- FORM_DRAWER: DRAWER_TYPES.FORM_DRAWER,
40
- MORE_ACTIONS_DRAWER: DRAWER_TYPES.MORE_ACTIONS_DRAWER,
41
- VIEW_DRAWER: DRAWER_TYPES.VIEW_DRAWER,
42
- };
43
- const feeStructureConfig = {
44
- name: "FeeStructure",
45
- displayName: "Fee Structure",
46
- drawerTypes: DRAWER_TYPES,
47
- initialState: {
48
- // List Data
49
- items: [],
50
- count: 0,
51
- // Search & Pagination
52
- currentPage: 1,
53
- pageLimit,
54
- searchQuery: "",
55
- // UI State
56
- disableSaveButton: false,
57
- drawer: null,
58
- // Form State
59
- amount: 0,
60
- classId: null,
61
- description: null,
62
- enabled: true,
63
- errors: {},
64
- feeType: FEE_TYPE.TUITION,
65
- filterEnabled: undefined,
66
- frequency: null,
67
- id: "",
68
- name: "",
69
- schoolId: "",
70
- },
71
- };
72
- // ============================================================================
73
- // 1.3 CREATE FEE STRUCTURE MODULE
74
- // ============================================================================
75
- export const { actionTypes: FEE_STRUCTURE_ACTION_TYPES, config: feeStructureModuleConfig, initialState: initialFeeStructureState, Provider: FeeStructureProvider, reducer: feeStructureReducer, useContext: useFeeStructureContext, } = createGenericModule(feeStructureConfig);
76
- // ============================================================================
77
- // 1.4 ENHANCED FEE STRUCTURE HOOK WITH API INTEGRATION
12
+ // ENHANCED FEE STRUCTURE HOOK WITH API INTEGRATION
78
13
  // ============================================================================
79
14
  export const useFeeStructureModule = () => {
80
15
  // ============================================================================
81
- // 1.4.1 STATE & CORE HOOKS
16
+ // 1.4.1 STATE & API HOOKS
82
17
  // ============================================================================
83
18
  var _a;
84
19
  const context = useFeeStructureContext();
85
20
  const { dispatch } = context;
86
21
  const state = context.state;
22
+ const { listFetchNow, listLoading, updateFetchNow, updateLoading, byIdFetchNow, byIdLoading, deleteFetchNow, deleteLoading, resetFormAndCloseDrawer, } = useFeeStructureApiContext();
87
23
  const t = useTranslations("feeStructure");
88
24
  const { theme } = useTheme();
89
25
  const workspace = getCachedWorkspaceSync();
90
26
  const schoolId = ((_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id) || "";
91
- const listFetchNowRef = useRef(null);
92
- const debouncedQuery = useDebounce(state.searchQuery, 800);
93
- // ============================================================================
94
- // 1.4.2 API PARAMETERS
95
- // ============================================================================
96
- const listParams = useMemo(() => (Object.assign(Object.assign({ currentPage: state.currentPage, pageLimit: state.pageLimit, schoolId }, (debouncedQuery ? { searchQuery: debouncedQuery } : {})), (state.filterEnabled !== undefined
97
- ? { filterEnabled: String(state.filterEnabled) }
98
- : {}))), [
99
- state.currentPage,
100
- state.filterEnabled,
101
- state.pageLimit,
102
- debouncedQuery,
103
- schoolId,
104
- ]);
105
- const updateParams = useMemo(() => (Object.assign(Object.assign(Object.assign({}, (state.id ? { id: state.id } : {})), (!state.id ? { schoolId } : {})), { amount: Number(state.amount), classId: state.classId || null, description: state.description || null, enabled: state.enabled, feeType: state.feeType, frequency: state.frequency || null, name: state.name })), [state, schoolId]);
106
- const byIdParams = useMemo(() => ({ id: state.id }), [state.id]);
107
- const deleteParams = useMemo(() => ({ id: state.id }), [state.id]);
108
27
  // ============================================================================
109
- // 1.4.3 UTILITIES
28
+ // 1.4.2 UTILITIES
110
29
  // ============================================================================
111
30
  const showToast = useCallback((description, variant) => {
112
31
  generateThemeToast({
@@ -115,99 +34,12 @@ export const useFeeStructureModule = () => {
115
34
  variant,
116
35
  });
117
36
  }, [theme]);
118
- const resetFormAndCloseDrawer = useCallback(() => {
119
- dispatch({ type: FEE_STRUCTURE_ACTION_TYPES.RESET_FORM });
120
- dispatch({
121
- type: FEE_STRUCTURE_ACTION_TYPES.SET_ERRORS,
122
- payload: { errors: {} },
123
- });
124
- dispatch({
125
- type: FEE_STRUCTURE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
126
- payload: { disabled: false },
127
- });
128
- dispatch({
129
- type: FEE_STRUCTURE_ACTION_TYPES.SET_DRAWER,
130
- payload: { drawer: null },
131
- });
132
- }, [dispatch]);
133
- // ============================================================================
134
- // 1.4.4 API CALLBACKS
135
- // ============================================================================
136
- const listCallback = useCallback(({ data, error }) => {
137
- var _a, _b;
138
- if (error) {
139
- showToast(t("messagesNetworkError"), TOAST_VARIANT.ERROR);
140
- return;
141
- }
142
- if (data) {
143
- const response = data;
144
- dispatch({
145
- type: FEE_STRUCTURE_ACTION_TYPES.SET_ITEMS,
146
- payload: { items: (_a = response.items) !== null && _a !== void 0 ? _a : [], count: (_b = response.count) !== null && _b !== void 0 ? _b : 0 },
147
- });
148
- }
149
- }, [dispatch, showToast, t]);
150
- const updateCallback = useCallback(({ data, error }) => {
151
- var _a;
152
- if (error) {
153
- showToast(t("messagesNetworkError"), TOAST_VARIANT.ERROR);
154
- return;
155
- }
156
- if (data) {
157
- const isCreated = isCreatedOrUpdated(data);
158
- showToast(isCreated
159
- ? t("messagesFeeStructureCreated")
160
- : t("messagesFeeStructureUpdated"), TOAST_VARIANT.SUCCESS);
161
- resetFormAndCloseDrawer();
162
- (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
163
- }
164
- }, [resetFormAndCloseDrawer, showToast, t]);
165
- const byIdCallback = useCallback(({ data, error }) => {
166
- if (error) {
167
- showToast(t("messagesNetworkError"), TOAST_VARIANT.ERROR);
168
- return;
169
- }
170
- if (data) {
171
- dispatch({
172
- type: FEE_STRUCTURE_ACTION_TYPES.SET_FORM_DATA,
173
- payload: { form: Object.assign(Object.assign({}, data), { filterEnabled: undefined }) },
174
- });
175
- }
176
- }, [dispatch, showToast, t]);
177
- const deleteCallback = useCallback(({ data, error }) => {
178
- var _a;
179
- if (error) {
180
- showToast(t("messagesNetworkError"), TOAST_VARIANT.ERROR);
181
- return;
182
- }
183
- if (data) {
184
- showToast(t("messagesFeeStructureDeleted"), TOAST_VARIANT.SUCCESS);
185
- (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
186
- }
187
- }, [showToast, t]);
188
37
  // ============================================================================
189
- // 1.4.5 API HOOKS
38
+ // 1.4.3 UPDATE PARAMS (used by handleSubmit for validation + fetch body)
190
39
  // ============================================================================
191
- const { byIdFetchNow, byIdLoading, deleteFetchNow, deleteLoading, listFetchNow, listLoading, updateFetchNow, updateLoading, } = useModuleEntityV2({
192
- byIdCallback,
193
- byIdParams,
194
- deleteCallback,
195
- deleteParams,
196
- listCallback,
197
- listParams,
198
- listUrl: FEE_STRUCTURE_API_ROUTES.UNIT,
199
- searchQuery: debouncedQuery,
200
- unitByIdUrl: FEE_STRUCTURE_API_ROUTES.UNIT,
201
- unitUrl: FEE_STRUCTURE_API_ROUTES.UNIT,
202
- updateCallback,
203
- updateParams,
204
- headers: {
205
- "Content-Type": "application/json",
206
- // "x-api-token": process.env.NEXT_PUBLIC_API_KEY!,
207
- },
208
- });
40
+ const updateParams = useMemo(() => (Object.assign(Object.assign(Object.assign({}, (state.id ? { id: state.id } : {})), (!state.id ? { schoolId } : {})), { amount: Number(state.amount), classId: state.classId || null, description: state.description || null, enabled: state.enabled, feeType: state.feeType, frequency: state.frequency || null, name: state.name })), [state, schoolId]);
209
41
  // ============================================================================
210
- // 1.4.6 HANDLERS
42
+ // 1.4.4 HANDLERS
211
43
  // ============================================================================
212
44
  const handleChange = useCallback((field, value) => {
213
45
  dispatch({
@@ -318,7 +150,7 @@ export const useFeeStructureModule = () => {
318
150
  });
319
151
  }, [updateFetchNow]);
320
152
  // ============================================================================
321
- // 1.4.7 NETWORK ACTIONS
153
+ // 1.4.5 NETWORK ACTIONS
322
154
  // ============================================================================
323
155
  const handleSubmit = useCallback(() => {
324
156
  dispatch({
@@ -347,7 +179,7 @@ export const useFeeStructureModule = () => {
347
179
  });
348
180
  }, [dispatch, updateParams, t, showToast, updateFetchNow]);
349
181
  // ============================================================================
350
- // 1.4.8 HEADER & ROW ACTIONS
182
+ // 1.4.6 HEADER & ROW ACTIONS
351
183
  // ============================================================================
352
184
  const headerActions = useMemo(() => [
353
185
  {
@@ -397,20 +229,7 @@ export const useFeeStructureModule = () => {
397
229
  },
398
230
  ], [handleDelete, handleEdit, handleView, t, toggleStatus]);
399
231
  // ============================================================================
400
- // 1.4.9 EFFECTS
401
- // ============================================================================
402
- // Sync ref to always point at latest listFetchNow (avoids stale closure in callbacks)
403
- useEffect(() => {
404
- listFetchNowRef.current = listFetchNow;
405
- }, [listFetchNow]);
406
- useEffect(() => {
407
- var _a;
408
- if (!schoolId)
409
- return;
410
- (_a = listFetchNowRef.current) === null || _a === void 0 ? void 0 : _a.call(listFetchNowRef);
411
- }, [dispatch, listParams, showToast, t, schoolId]);
412
- // ============================================================================
413
- // 1.4.10 RETURN
232
+ // 1.4.7 RETURN
414
233
  // ============================================================================
415
234
  return Object.assign(Object.assign({}, context), { applyFilters,
416
235
  byIdLoading,
@@ -17,7 +17,11 @@
17
17
  */
18
18
  import { FC } from "react";
19
19
  import { USER_ROLE } from "../../type";
20
- interface Props {
20
+ interface ConfigProps {
21
+ dispatch: React.Dispatch<{
22
+ type: string;
23
+ payload?: unknown;
24
+ }>;
21
25
  drawerButtonCancel: string;
22
26
  drawerButtonSave: string;
23
27
  drawerFilterDescription: string;
@@ -39,6 +43,8 @@ interface Props {
39
43
  tableDescription: string;
40
44
  tableSearchPlaceholder: string;
41
45
  tableTitle: string;
46
+ }
47
+ interface Props extends ConfigProps {
42
48
  userRole: USER_ROLE;
43
49
  }
44
50
  export declare const FeeStructurePage: FC<Props>;
@@ -20,7 +20,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
20
20
  import { useMemo } from "react";
21
21
  import { COMPONENT_TYPE } from "@appcorp/shadcn/components/enhanced-table";
22
22
  import { createGenericModulePage, } from "@react-pakistan/util-functions/factory/generic-component-factory";
23
- import { useFeeStructureModule, FeeStructureProvider, FEE_STRUCTURE_ACTION_TYPES, } from "./context";
23
+ import { useFeeStructureModule, FeeStructureProvider, FeeStructureApiProvider, FEE_STRUCTURE_ACTION_TYPES, } from "./context";
24
24
  import { FeeStructureFilter } from "./filter";
25
25
  import { FeeStructureForm } from "./form";
26
26
  import { FeeStructureMoreActions } from "./more-actions";
@@ -106,6 +106,9 @@ const createFeeStructureConfig = ({ dispatch, drawerButtonCancel, drawerButtonSa
106
106
  // STABLE PAGE COMPONENT (created once, outside render)
107
107
  // ============================================================================
108
108
  const GenericFeeStructurePage = createGenericModulePage();
109
+ // ============================================================================
110
+ // INNER PAGE (requires FeeStructureProvider context)
111
+ // ============================================================================
109
112
  const FeeStructurePageInner = (props) => {
110
113
  const context = useFeeStructureModule();
111
114
  const feeStructureConfig = useMemo(() => createFeeStructureConfig({
@@ -167,4 +170,4 @@ const FeeStructurePageInner = (props) => {
167
170
  // ============================================================================
168
171
  // PAGE EXPORTS
169
172
  // ============================================================================
170
- export const FeeStructurePage = (props) => (_jsx(FeeStructureProvider, { children: _jsx(FeeStructurePageInner, Object.assign({}, props)) }));
173
+ export const FeeStructurePage = (props) => (_jsx(FeeStructureProvider, { children: _jsx(FeeStructureApiProvider, { children: _jsx(FeeStructurePageInner, Object.assign({}, props)) }) }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appcorp/fusion-storybook",
3
- "version": "0.3.90",
3
+ "version": "0.3.91",
4
4
  "scripts": {
5
5
  "build-storybook": "storybook build",
6
6
  "build:next": "next build",