@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.
- package/README.md +147 -0
- package/base-modules/admission/cache.d.ts +14 -0
- package/base-modules/admission/cache.js +31 -0
- package/base-modules/admission/constants.d.ts +32 -0
- package/base-modules/admission/constants.js +37 -0
- package/base-modules/admission/context.d.ts +369 -0
- package/base-modules/admission/context.js +755 -0
- package/base-modules/admission/filter.d.ts +1 -0
- package/base-modules/admission/filter.js +31 -0
- package/base-modules/admission/form.d.ts +1 -0
- package/base-modules/admission/form.js +86 -0
- package/base-modules/admission/more-actions.d.ts +1 -0
- package/base-modules/admission/more-actions.js +5 -0
- package/base-modules/admission/page.d.ts +16 -0
- package/base-modules/admission/page.js +128 -0
- package/base-modules/admission/validate.d.ts +38 -0
- package/base-modules/admission/validate.js +70 -0
- package/base-modules/admission/view.d.ts +1 -0
- package/base-modules/admission/view.js +40 -0
- package/base-modules/discount-code/cache.d.ts +27 -0
- package/base-modules/discount-code/cache.js +46 -0
- package/base-modules/discount-code/constants.d.ts +19 -0
- package/base-modules/discount-code/constants.js +26 -0
- package/base-modules/discount-code/context.d.ts +154 -0
- package/base-modules/discount-code/context.js +416 -0
- package/base-modules/discount-code/filter.d.ts +9 -0
- package/base-modules/discount-code/filter.js +14 -0
- package/base-modules/discount-code/form.d.ts +1 -0
- package/base-modules/discount-code/form.js +44 -0
- package/base-modules/discount-code/more-actions.d.ts +9 -0
- package/base-modules/discount-code/more-actions.js +13 -0
- package/base-modules/discount-code/page.d.ts +16 -0
- package/base-modules/discount-code/page.js +120 -0
- package/base-modules/discount-code/validate.d.ts +19 -0
- package/base-modules/discount-code/validate.js +38 -0
- package/base-modules/discount-code/view.d.ts +1 -0
- package/base-modules/discount-code/view.js +26 -0
- package/base-modules/family/cache.d.ts +14 -0
- package/base-modules/family/cache.js +31 -0
- package/base-modules/family/constants.d.ts +22 -0
- package/base-modules/family/constants.js +26 -0
- package/base-modules/family/context.d.ts +173 -0
- package/base-modules/family/context.js +441 -0
- package/base-modules/family/filter.d.ts +1 -0
- package/base-modules/family/filter.js +28 -0
- package/base-modules/family/form.d.ts +1 -0
- package/base-modules/family/form.js +12 -0
- package/base-modules/family/more-actions.d.ts +1 -0
- package/base-modules/family/more-actions.js +5 -0
- package/base-modules/family/page.d.ts +16 -0
- package/base-modules/family/page.js +120 -0
- package/base-modules/family/validate.d.ts +15 -0
- package/base-modules/family/validate.js +18 -0
- package/base-modules/family/view.d.ts +1 -0
- package/base-modules/family/view.js +18 -0
- package/base-modules/family-member/cache.d.ts +14 -0
- package/base-modules/family-member/cache.js +31 -0
- package/base-modules/family-member/constants.d.ts +22 -0
- package/base-modules/family-member/constants.js +26 -0
- package/base-modules/family-member/context.d.ts +215 -0
- package/base-modules/family-member/context.js +486 -0
- package/base-modules/family-member/filter.d.ts +1 -0
- package/base-modules/family-member/filter.js +28 -0
- package/base-modules/family-member/form.d.ts +1 -0
- package/base-modules/family-member/form.js +17 -0
- package/base-modules/family-member/more-actions.d.ts +1 -0
- package/base-modules/family-member/more-actions.js +5 -0
- package/base-modules/family-member/page.d.ts +16 -0
- package/base-modules/family-member/page.js +122 -0
- package/base-modules/family-member/validate.d.ts +24 -0
- package/base-modules/family-member/validate.js +27 -0
- package/base-modules/family-member/view.d.ts +1 -0
- package/base-modules/family-member/view.js +40 -0
- package/base-modules/student-profile/cache.d.ts +14 -0
- package/base-modules/student-profile/cache.js +31 -0
- package/base-modules/student-profile/constants.d.ts +105 -0
- package/base-modules/student-profile/constants.js +132 -0
- package/base-modules/student-profile/context.d.ts +290 -0
- package/base-modules/student-profile/context.js +583 -0
- package/base-modules/student-profile/filter.d.ts +1 -0
- package/base-modules/student-profile/filter.js +26 -0
- package/base-modules/student-profile/form.d.ts +1 -0
- package/base-modules/student-profile/form.js +19 -0
- package/base-modules/student-profile/more-actions.d.ts +1 -0
- package/base-modules/student-profile/more-actions.js +5 -0
- package/base-modules/student-profile/page.d.ts +9 -0
- package/base-modules/student-profile/page.js +86 -0
- package/base-modules/student-profile/validate.d.ts +23 -0
- package/base-modules/student-profile/validate.js +34 -0
- package/base-modules/student-profile/view.d.ts +1 -0
- package/base-modules/student-profile/view.js +29 -0
- package/base-modules/workspace/cache.d.ts +9 -0
- package/base-modules/workspace/cache.js +28 -0
- package/base-modules/workspace/constants.d.ts +10 -0
- package/base-modules/workspace/constants.js +18 -0
- package/base-modules/workspace/context.d.ts +187 -0
- package/base-modules/workspace/context.js +293 -0
- package/base-modules/workspace/drawer.d.ts +1 -0
- package/base-modules/workspace/drawer.js +24 -0
- package/base-modules/workspace/filter.d.ts +1 -0
- package/base-modules/workspace/filter.js +13 -0
- package/base-modules/workspace/form.d.ts +1 -0
- package/base-modules/workspace/form.js +40 -0
- package/base-modules/workspace/more-actions.d.ts +1 -0
- package/base-modules/workspace/more-actions.js +13 -0
- package/base-modules/workspace/page.d.ts +1 -0
- package/base-modules/workspace/page.js +31 -0
- package/base-modules/workspace/validate.d.ts +9 -0
- package/base-modules/workspace/validate.js +8 -0
- package/base-modules/workspace/view.d.ts +1 -0
- package/base-modules/workspace/view.js +52 -0
- package/components/rbac-no-access.d.ts +6 -0
- package/components/rbac-no-access.js +4 -0
- package/constants.d.ts +81 -0
- package/constants.js +81 -0
- package/lib/utils.d.ts +2 -0
- package/lib/utils.js +5 -0
- package/package.json +104 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/type.d.ts +1141 -0
- package/type.js +427 -0
- package/utils/admission-pdf.d.ts +78 -0
- package/utils/admission-pdf.js +73 -0
- package/utils/clear-cache.d.ts +1 -0
- package/utils/clear-cache.js +8 -0
- package/utils/format-value.d.ts +1 -0
- package/utils/format-value.js +1 -0
- package/utils/pdf-generator.d.ts +41 -0
- package/utils/pdf-generator.js +107 -0
- package/utils/resolve-rbac-permissions.d.ts +11 -0
- package/utils/resolve-rbac-permissions.js +251 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { type RowAction, type TableRow } from "@appcorp/shadcn/components/enhanced-table";
|
|
2
|
+
import { DiscountCodeBE } from "@/type";
|
|
3
|
+
export declare const DISCOUNT_CODE_DRAWER: {
|
|
4
|
+
readonly FILTER_DRAWER: string;
|
|
5
|
+
readonly FORM_DRAWER: string;
|
|
6
|
+
readonly MORE_ACTIONS_DRAWER: string;
|
|
7
|
+
readonly VIEW_DRAWER: string;
|
|
8
|
+
};
|
|
9
|
+
export declare const DISCOUNT_CODE_ACTION_TYPES: {
|
|
10
|
+
readonly RESET_FORM: "RESET_FORM";
|
|
11
|
+
readonly SET_CURRENT_PAGE: "SET_CURRENT_PAGE";
|
|
12
|
+
readonly SET_PAGE_LIMIT: "SET_PAGE_LIMIT";
|
|
13
|
+
readonly SET_SEARCH_QUERY: "SET_SEARCH_QUERY";
|
|
14
|
+
readonly SET_DRAWER: "SET_DRAWER";
|
|
15
|
+
readonly SET_ITEMS: "SET_ITEMS";
|
|
16
|
+
readonly SET_FORM_DATA: "SET_FORM_DATA";
|
|
17
|
+
readonly SET_DISABLE_SAVE_BUTTON: "SET_DISABLE_SAVE_BUTTON";
|
|
18
|
+
readonly SET_INPUT_FIELD: "SET_INPUT_FIELD";
|
|
19
|
+
readonly SET_ERRORS: "SET_ERRORS";
|
|
20
|
+
readonly SET_FILTERS: "SET_FILTERS";
|
|
21
|
+
}, discountCodeModuleConfig: import("@react-pakistan/util-functions/factory/generic-module-factory").ModuleConfig<{
|
|
22
|
+
items: DiscountCodeBE[];
|
|
23
|
+
count: number;
|
|
24
|
+
currentPage: number;
|
|
25
|
+
pageLimit: number;
|
|
26
|
+
searchQuery: string;
|
|
27
|
+
disableSaveButton: boolean;
|
|
28
|
+
drawer: null;
|
|
29
|
+
modal: null;
|
|
30
|
+
code: string;
|
|
31
|
+
description: string;
|
|
32
|
+
discountType: string;
|
|
33
|
+
discountValue: number;
|
|
34
|
+
enabled: boolean;
|
|
35
|
+
errors: Record<string, string>;
|
|
36
|
+
filterEnabled: boolean | undefined;
|
|
37
|
+
id: string;
|
|
38
|
+
}>, initialDiscountCodeState: {
|
|
39
|
+
items: DiscountCodeBE[];
|
|
40
|
+
count: number;
|
|
41
|
+
currentPage: number;
|
|
42
|
+
pageLimit: number;
|
|
43
|
+
searchQuery: string;
|
|
44
|
+
disableSaveButton: boolean;
|
|
45
|
+
drawer: null;
|
|
46
|
+
modal: null;
|
|
47
|
+
code: string;
|
|
48
|
+
description: string;
|
|
49
|
+
discountType: string;
|
|
50
|
+
discountValue: number;
|
|
51
|
+
enabled: boolean;
|
|
52
|
+
errors: Record<string, string>;
|
|
53
|
+
filterEnabled: boolean | undefined;
|
|
54
|
+
id: string;
|
|
55
|
+
}, DiscountCodeProvider: import("react").FC<{
|
|
56
|
+
children: React.ReactNode;
|
|
57
|
+
}>, discountCodeReducer: (state: {
|
|
58
|
+
items: DiscountCodeBE[];
|
|
59
|
+
count: number;
|
|
60
|
+
currentPage: number;
|
|
61
|
+
pageLimit: number;
|
|
62
|
+
searchQuery: string;
|
|
63
|
+
disableSaveButton: boolean;
|
|
64
|
+
drawer: null;
|
|
65
|
+
modal: null;
|
|
66
|
+
code: string;
|
|
67
|
+
description: string;
|
|
68
|
+
discountType: string;
|
|
69
|
+
discountValue: number;
|
|
70
|
+
enabled: boolean;
|
|
71
|
+
errors: Record<string, string>;
|
|
72
|
+
filterEnabled: boolean | undefined;
|
|
73
|
+
id: string;
|
|
74
|
+
}, action: any) => {
|
|
75
|
+
items: DiscountCodeBE[];
|
|
76
|
+
count: number;
|
|
77
|
+
currentPage: number;
|
|
78
|
+
pageLimit: number;
|
|
79
|
+
searchQuery: string;
|
|
80
|
+
disableSaveButton: boolean;
|
|
81
|
+
drawer: null;
|
|
82
|
+
modal: null;
|
|
83
|
+
code: string;
|
|
84
|
+
description: string;
|
|
85
|
+
discountType: string;
|
|
86
|
+
discountValue: number;
|
|
87
|
+
enabled: boolean;
|
|
88
|
+
errors: Record<string, string>;
|
|
89
|
+
filterEnabled: boolean | undefined;
|
|
90
|
+
id: string;
|
|
91
|
+
}, useDiscountCodeStateContext: () => import("@react-pakistan/util-functions/factory/generic-module-factory").GenericModuleContext<{
|
|
92
|
+
items: DiscountCodeBE[];
|
|
93
|
+
count: number;
|
|
94
|
+
currentPage: number;
|
|
95
|
+
pageLimit: number;
|
|
96
|
+
searchQuery: string;
|
|
97
|
+
disableSaveButton: boolean;
|
|
98
|
+
drawer: null;
|
|
99
|
+
modal: null;
|
|
100
|
+
code: string;
|
|
101
|
+
description: string;
|
|
102
|
+
discountType: string;
|
|
103
|
+
discountValue: number;
|
|
104
|
+
enabled: boolean;
|
|
105
|
+
errors: Record<string, string>;
|
|
106
|
+
filterEnabled: boolean | undefined;
|
|
107
|
+
id: string;
|
|
108
|
+
}>;
|
|
109
|
+
export declare const useDiscountCodeModule: () => {
|
|
110
|
+
applyFilters: () => void;
|
|
111
|
+
byIdLoading: boolean;
|
|
112
|
+
clearFilters: () => void;
|
|
113
|
+
deleteLoading: boolean;
|
|
114
|
+
handleChange: (field: string, value: string | number | boolean | undefined) => void;
|
|
115
|
+
handleCloseDrawer: () => void;
|
|
116
|
+
handleCreate: () => void;
|
|
117
|
+
handleDelete: (row?: TableRow) => void;
|
|
118
|
+
handleEdit: (row?: TableRow) => void;
|
|
119
|
+
handleFilters: () => void;
|
|
120
|
+
handlePageChange: (page: number) => void;
|
|
121
|
+
handlePageLimitChange: (limit: number) => void;
|
|
122
|
+
handleSearch: (query: string) => void;
|
|
123
|
+
handleSubmit: () => void;
|
|
124
|
+
handleView: (row?: TableRow) => void;
|
|
125
|
+
headerActions: {
|
|
126
|
+
enabled: boolean;
|
|
127
|
+
handleOnClick: () => void;
|
|
128
|
+
label: string;
|
|
129
|
+
order: number;
|
|
130
|
+
icon: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
|
|
131
|
+
}[];
|
|
132
|
+
listLoading: boolean;
|
|
133
|
+
rowActions: RowAction[];
|
|
134
|
+
updateLoading: boolean;
|
|
135
|
+
state: {
|
|
136
|
+
items: DiscountCodeBE[];
|
|
137
|
+
count: number;
|
|
138
|
+
currentPage: number;
|
|
139
|
+
pageLimit: number;
|
|
140
|
+
searchQuery: string;
|
|
141
|
+
disableSaveButton: boolean;
|
|
142
|
+
drawer: null;
|
|
143
|
+
modal: null;
|
|
144
|
+
code: string;
|
|
145
|
+
description: string;
|
|
146
|
+
discountType: string;
|
|
147
|
+
discountValue: number;
|
|
148
|
+
enabled: boolean;
|
|
149
|
+
errors: Record<string, string>;
|
|
150
|
+
filterEnabled: boolean | undefined;
|
|
151
|
+
id: string;
|
|
152
|
+
};
|
|
153
|
+
dispatch: React.Dispatch<any>;
|
|
154
|
+
};
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* Discount Code Module — business logic + API integration
|
|
4
|
+
*
|
|
5
|
+
* Wires the generic module state (created by `createGenericModule`) to network
|
|
6
|
+
* actions using `useModuleEntityV2`. Contains domain-specific handlers for
|
|
7
|
+
* create/view/edit/delete operations.
|
|
8
|
+
*
|
|
9
|
+
* Key responsibilities:
|
|
10
|
+
* - expose `useDiscountCodeModule()` which UI components call for actions
|
|
11
|
+
* - keep module-specific `apiParams` and callbacks in one place
|
|
12
|
+
* - ensure cache invalidation and toast notifications on mutation
|
|
13
|
+
*
|
|
14
|
+
* Exported utilities:
|
|
15
|
+
* - `DiscountCodeProvider` — provider component used by the page
|
|
16
|
+
* - `useDiscountCodeModule()` — hook that returns state, dispatch, and handlers
|
|
17
|
+
*/
|
|
18
|
+
import { useCallback, useEffect, useMemo } from "react";
|
|
19
|
+
import { isCreatedOrUpdated, validateForm, } from "@react-pakistan/util-functions";
|
|
20
|
+
import { useModuleEntityV2, } from "@react-pakistan/util-functions/hooks/use-module-entity-v2";
|
|
21
|
+
import { useDebounce } from "@react-pakistan/util-functions/hooks/use-debounce";
|
|
22
|
+
import { createGenericModule } from "@react-pakistan/util-functions/factory/generic-module-factory";
|
|
23
|
+
import { DRAWER_TYPES } from "@react-pakistan/util-functions/factory/generic-component-factory";
|
|
24
|
+
import { DISCOUNT_CODE_API_ROUTES, pageLimit } from "./constants";
|
|
25
|
+
import { getCachedDiscountCodes, invalidateDiscountCodeCache } from "./cache";
|
|
26
|
+
import { accountDiscountCodeFormValidation } from "./validate";
|
|
27
|
+
import { generateThemeToast, TOAST_VARIANT, } from "@appcorp/shadcn/lib/toast-utils";
|
|
28
|
+
import { useTranslations } from "next-intl";
|
|
29
|
+
import { getCachedWorkspaceSync } from "../workspace/cache";
|
|
30
|
+
import { Filter, Plus } from "lucide-react";
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// DRAWER TYPES
|
|
33
|
+
// ============================================================================
|
|
34
|
+
export const DISCOUNT_CODE_DRAWER = {
|
|
35
|
+
FILTER_DRAWER: DRAWER_TYPES.FILTER_DRAWER,
|
|
36
|
+
FORM_DRAWER: DRAWER_TYPES.FORM_DRAWER,
|
|
37
|
+
MORE_ACTIONS_DRAWER: DRAWER_TYPES.MORE_ACTIONS_DRAWER,
|
|
38
|
+
VIEW_DRAWER: DRAWER_TYPES.VIEW_DRAWER,
|
|
39
|
+
};
|
|
40
|
+
// ============================================================================
|
|
41
|
+
// CONFIGURATION
|
|
42
|
+
// ============================================================================
|
|
43
|
+
const discountCodeConfig = {
|
|
44
|
+
name: "DiscountCode",
|
|
45
|
+
displayName: "Discount Code",
|
|
46
|
+
initialState: {
|
|
47
|
+
// List Data
|
|
48
|
+
items: [],
|
|
49
|
+
count: 0,
|
|
50
|
+
// Search & Pagination
|
|
51
|
+
currentPage: 1,
|
|
52
|
+
pageLimit,
|
|
53
|
+
searchQuery: "",
|
|
54
|
+
// UI State
|
|
55
|
+
disableSaveButton: false,
|
|
56
|
+
drawer: null,
|
|
57
|
+
modal: null,
|
|
58
|
+
// Form State
|
|
59
|
+
code: "",
|
|
60
|
+
description: "",
|
|
61
|
+
discountType: "",
|
|
62
|
+
discountValue: 0,
|
|
63
|
+
enabled: true,
|
|
64
|
+
errors: {},
|
|
65
|
+
filterEnabled: undefined,
|
|
66
|
+
id: "",
|
|
67
|
+
},
|
|
68
|
+
drawerTypes: DRAWER_TYPES,
|
|
69
|
+
};
|
|
70
|
+
// ============================================================================
|
|
71
|
+
// CREATE DISCOUNT CODE MODULE
|
|
72
|
+
// ============================================================================
|
|
73
|
+
export const { actionTypes: DISCOUNT_CODE_ACTION_TYPES, config: discountCodeModuleConfig, initialState: initialDiscountCodeState, Provider: DiscountCodeProvider, reducer: discountCodeReducer, useContext: useDiscountCodeStateContext, } = createGenericModule(discountCodeConfig);
|
|
74
|
+
// ============================================================================
|
|
75
|
+
// ENHANCED DISCOUNT CODE HOOK WITH API INTEGRATION
|
|
76
|
+
// ============================================================================
|
|
77
|
+
export const useDiscountCodeModule = () => {
|
|
78
|
+
var _a;
|
|
79
|
+
const context = useDiscountCodeStateContext();
|
|
80
|
+
const t = useTranslations("discountCode");
|
|
81
|
+
const workspace = getCachedWorkspaceSync();
|
|
82
|
+
const schoolId = ((_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id) || "";
|
|
83
|
+
const debouncedQuery = useDebounce(context.state.searchQuery, 800);
|
|
84
|
+
// ============================================================================
|
|
85
|
+
// API PARAMETERS
|
|
86
|
+
// ============================================================================
|
|
87
|
+
const listParams = useMemo(() => (Object.assign(Object.assign({ currentPage: context.state.currentPage, pageLimit: context.state.pageLimit, schoolId }, (debouncedQuery ? { searchQuery: debouncedQuery } : {})), (context.state.filterEnabled !== undefined
|
|
88
|
+
? { filterEnabled: String(context.state.filterEnabled) }
|
|
89
|
+
: {}))), [
|
|
90
|
+
context.state.currentPage,
|
|
91
|
+
context.state.pageLimit,
|
|
92
|
+
context.state.filterEnabled,
|
|
93
|
+
debouncedQuery,
|
|
94
|
+
schoolId,
|
|
95
|
+
]);
|
|
96
|
+
const updateParams = useMemo(() => ({
|
|
97
|
+
code: context.state.code,
|
|
98
|
+
description: context.state.description,
|
|
99
|
+
discountType: context.state.discountType,
|
|
100
|
+
discountValue: context.state.discountValue,
|
|
101
|
+
enabled: context.state.enabled,
|
|
102
|
+
id: context.state.id,
|
|
103
|
+
schoolId,
|
|
104
|
+
}), [
|
|
105
|
+
context.state.code,
|
|
106
|
+
context.state.description,
|
|
107
|
+
context.state.discountType,
|
|
108
|
+
context.state.discountValue,
|
|
109
|
+
context.state.enabled,
|
|
110
|
+
context.state.id,
|
|
111
|
+
schoolId,
|
|
112
|
+
]);
|
|
113
|
+
const byIdParams = useMemo(() => ({ id: context.state.id }), [context.state.id]);
|
|
114
|
+
const deleteParams = useMemo(() => ({ id: context.state.id }), [context.state.id]);
|
|
115
|
+
// ============================================================================
|
|
116
|
+
// API CALLBACKS
|
|
117
|
+
// ============================================================================
|
|
118
|
+
const listCallback = ({ data, error }) => {
|
|
119
|
+
if (error) {
|
|
120
|
+
showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (data) {
|
|
124
|
+
context.dispatch({
|
|
125
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_ITEMS,
|
|
126
|
+
payload: { items: data.items || [], count: data.count || 0 },
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
const updateCallback = ({ data, error }) => {
|
|
131
|
+
const isCreated = isCreatedOrUpdated(data);
|
|
132
|
+
if (error) {
|
|
133
|
+
showToast(isCreated ? t("messagesCreateFailed") : t("messagesUpdateFailed"), TOAST_VARIANT.ERROR);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (data) {
|
|
137
|
+
showToast(t("messagesSaveSuccess"), TOAST_VARIANT.SUCCESS);
|
|
138
|
+
invalidateDiscountCodeCache();
|
|
139
|
+
listFetchNow();
|
|
140
|
+
resetFormAndCloseDrawer();
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
const byIdCallback = ({ data, error }) => {
|
|
144
|
+
if (error) {
|
|
145
|
+
showToast(t("messagesDetailsFetchFailed"), TOAST_VARIANT.ERROR);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (data) {
|
|
149
|
+
const discountCode = data;
|
|
150
|
+
// Batch field updates for better performance
|
|
151
|
+
const fieldMappings = {
|
|
152
|
+
id: discountCode.id,
|
|
153
|
+
code: discountCode.code,
|
|
154
|
+
description: discountCode.description,
|
|
155
|
+
discountType: discountCode.discountType,
|
|
156
|
+
discountValue: discountCode.discountValue,
|
|
157
|
+
enabled: discountCode.enabled,
|
|
158
|
+
};
|
|
159
|
+
Object.entries(fieldMappings).forEach(([key, value]) => {
|
|
160
|
+
setField(key, value !== null && value !== void 0 ? value : undefined);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
const deleteCallback = ({ data, error }) => {
|
|
165
|
+
if (error) {
|
|
166
|
+
showToast(t("messagesDeleteFailed"), TOAST_VARIANT.ERROR);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (data) {
|
|
170
|
+
showToast(t("messagesDeleteSuccess"), TOAST_VARIANT.SUCCESS);
|
|
171
|
+
invalidateDiscountCodeCache();
|
|
172
|
+
listFetchNow();
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
// ============================================================================
|
|
176
|
+
// API HOOKS
|
|
177
|
+
// ============================================================================
|
|
178
|
+
const { listFetchNow, listLoading, updateFetchNow, updateLoading, byIdFetchNow, deleteFetchNow, deleteLoading, byIdLoading, } = useModuleEntityV2({
|
|
179
|
+
byIdCallback,
|
|
180
|
+
byIdParams,
|
|
181
|
+
deleteCallback,
|
|
182
|
+
deleteParams,
|
|
183
|
+
listCallback,
|
|
184
|
+
listParams,
|
|
185
|
+
listUrl: DISCOUNT_CODE_API_ROUTES.UNIT,
|
|
186
|
+
searchQuery: debouncedQuery,
|
|
187
|
+
unitByIdUrl: DISCOUNT_CODE_API_ROUTES.UNIT,
|
|
188
|
+
unitUrl: DISCOUNT_CODE_API_ROUTES.UNIT,
|
|
189
|
+
updateCallback,
|
|
190
|
+
updateParams,
|
|
191
|
+
headers: {
|
|
192
|
+
"Content-Type": "application/json",
|
|
193
|
+
"x-api-token": process.env.NEXT_PUBLIC_API_KEY,
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
// ============================================================================
|
|
197
|
+
// UTILITIES
|
|
198
|
+
// ============================================================================
|
|
199
|
+
const showToast = useCallback((message, variant) => {
|
|
200
|
+
generateThemeToast({ description: message, variant });
|
|
201
|
+
}, []);
|
|
202
|
+
const resetFormAndCloseDrawer = useCallback(() => {
|
|
203
|
+
context.dispatch({
|
|
204
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_ERRORS,
|
|
205
|
+
payload: { errors: {} },
|
|
206
|
+
});
|
|
207
|
+
context.dispatch({
|
|
208
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
|
|
209
|
+
payload: { disabled: false },
|
|
210
|
+
});
|
|
211
|
+
context.dispatch({
|
|
212
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_DRAWER,
|
|
213
|
+
payload: { drawer: null },
|
|
214
|
+
});
|
|
215
|
+
}, [context]);
|
|
216
|
+
const setField = useCallback((key, value) => {
|
|
217
|
+
context.dispatch({
|
|
218
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_INPUT_FIELD,
|
|
219
|
+
payload: { field: key, value },
|
|
220
|
+
});
|
|
221
|
+
}, [context]);
|
|
222
|
+
// ============================================================================
|
|
223
|
+
// HANDLERS
|
|
224
|
+
// ============================================================================
|
|
225
|
+
const handleChange = useCallback((field, value) => {
|
|
226
|
+
context.dispatch({
|
|
227
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_ERRORS,
|
|
228
|
+
payload: { errors: {} },
|
|
229
|
+
});
|
|
230
|
+
context.dispatch({
|
|
231
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
|
|
232
|
+
payload: { disabled: false },
|
|
233
|
+
});
|
|
234
|
+
setField(field, value);
|
|
235
|
+
}, [setField, context]);
|
|
236
|
+
const handleCloseDrawer = useCallback(() => {
|
|
237
|
+
resetFormAndCloseDrawer();
|
|
238
|
+
}, [resetFormAndCloseDrawer]);
|
|
239
|
+
const handleCreate = useCallback(() => {
|
|
240
|
+
context.dispatch({
|
|
241
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_DRAWER,
|
|
242
|
+
payload: { drawer: DISCOUNT_CODE_DRAWER.FORM_DRAWER },
|
|
243
|
+
});
|
|
244
|
+
}, [context]);
|
|
245
|
+
const handleView = useCallback((row) => {
|
|
246
|
+
byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: row === null || row === void 0 ? void 0 : row.id } });
|
|
247
|
+
context.dispatch({
|
|
248
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_DRAWER,
|
|
249
|
+
payload: { drawer: DISCOUNT_CODE_DRAWER.VIEW_DRAWER },
|
|
250
|
+
});
|
|
251
|
+
}, [context, byIdFetchNow]);
|
|
252
|
+
const handleEdit = useCallback((row) => {
|
|
253
|
+
byIdFetchNow === null || byIdFetchNow === void 0 ? void 0 : byIdFetchNow(undefined, { params: { id: row === null || row === void 0 ? void 0 : row.id } });
|
|
254
|
+
context.dispatch({
|
|
255
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_DRAWER,
|
|
256
|
+
payload: { drawer: DISCOUNT_CODE_DRAWER.FORM_DRAWER },
|
|
257
|
+
});
|
|
258
|
+
}, [context, byIdFetchNow]);
|
|
259
|
+
const handleDelete = useCallback((row) => {
|
|
260
|
+
if (confirm(t("confirmDelete"))) {
|
|
261
|
+
deleteFetchNow === null || deleteFetchNow === void 0 ? void 0 : deleteFetchNow(undefined, {
|
|
262
|
+
body: JSON.stringify({ id: row === null || row === void 0 ? void 0 : row.id }),
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}, [t, deleteFetchNow]);
|
|
266
|
+
const handleFilters = useCallback(() => {
|
|
267
|
+
context.dispatch({
|
|
268
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_DRAWER,
|
|
269
|
+
payload: { drawer: DISCOUNT_CODE_DRAWER.FILTER_DRAWER },
|
|
270
|
+
});
|
|
271
|
+
}, [context]);
|
|
272
|
+
const handlePageChange = useCallback((page) => {
|
|
273
|
+
context.dispatch({
|
|
274
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_CURRENT_PAGE,
|
|
275
|
+
payload: { currentPage: page },
|
|
276
|
+
});
|
|
277
|
+
}, [context]);
|
|
278
|
+
const handlePageLimitChange = useCallback((limit) => {
|
|
279
|
+
context.dispatch({
|
|
280
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_PAGE_LIMIT,
|
|
281
|
+
payload: { pageLimit: limit },
|
|
282
|
+
});
|
|
283
|
+
}, [context]);
|
|
284
|
+
const handleSearch = useCallback((query) => {
|
|
285
|
+
context.dispatch({
|
|
286
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_SEARCH_QUERY,
|
|
287
|
+
payload: { searchQuery: query },
|
|
288
|
+
});
|
|
289
|
+
}, [context]);
|
|
290
|
+
const clearFilters = useCallback(() => {
|
|
291
|
+
context.dispatch({
|
|
292
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_FILTERS,
|
|
293
|
+
payload: { filters: { filterEnabled: undefined } },
|
|
294
|
+
});
|
|
295
|
+
context.dispatch({
|
|
296
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_CURRENT_PAGE,
|
|
297
|
+
payload: { currentPage: 1 },
|
|
298
|
+
});
|
|
299
|
+
}, [context]);
|
|
300
|
+
const handleSubmit = useCallback(() => {
|
|
301
|
+
context.dispatch({
|
|
302
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
|
|
303
|
+
payload: { disabled: true },
|
|
304
|
+
});
|
|
305
|
+
validateForm({
|
|
306
|
+
params: updateParams,
|
|
307
|
+
schema: accountDiscountCodeFormValidation,
|
|
308
|
+
successCallback: () => {
|
|
309
|
+
updateFetchNow(undefined, {
|
|
310
|
+
body: JSON.stringify(updateParams),
|
|
311
|
+
});
|
|
312
|
+
},
|
|
313
|
+
errorCallback: (errors) => {
|
|
314
|
+
context.dispatch({
|
|
315
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_ERRORS,
|
|
316
|
+
payload: { errors },
|
|
317
|
+
});
|
|
318
|
+
context.dispatch({
|
|
319
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_DISABLE_SAVE_BUTTON,
|
|
320
|
+
payload: { disabled: false },
|
|
321
|
+
});
|
|
322
|
+
showToast(t("messagesFormErrors"), TOAST_VARIANT.ERROR);
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
}, [context, updateParams, t, showToast, updateFetchNow]);
|
|
326
|
+
// ============================================================================
|
|
327
|
+
// HEADER & ROW ACTIONS
|
|
328
|
+
// ============================================================================
|
|
329
|
+
const headerActions = useMemo(() => [
|
|
330
|
+
{
|
|
331
|
+
enabled: true,
|
|
332
|
+
handleOnClick: handleFilters,
|
|
333
|
+
label: t("headerActionsFilters"),
|
|
334
|
+
order: 1,
|
|
335
|
+
icon: Filter,
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
enabled: true,
|
|
339
|
+
handleOnClick: handleCreate,
|
|
340
|
+
label: t("headerActionsAdd"),
|
|
341
|
+
order: 2,
|
|
342
|
+
icon: Plus,
|
|
343
|
+
},
|
|
344
|
+
], [handleFilters, handleCreate, t]);
|
|
345
|
+
const rowActions = useMemo(() => [
|
|
346
|
+
{
|
|
347
|
+
enabled: true,
|
|
348
|
+
handleOnClick: handleView,
|
|
349
|
+
label: t("rowActionsView"),
|
|
350
|
+
order: 1,
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
enabled: true,
|
|
354
|
+
handleOnClick: handleEdit,
|
|
355
|
+
label: t("rowActionsEdit"),
|
|
356
|
+
order: 2,
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
enabled: true,
|
|
360
|
+
handleOnClick: handleDelete,
|
|
361
|
+
label: t("rowActionsDelete"),
|
|
362
|
+
order: 3,
|
|
363
|
+
},
|
|
364
|
+
], [handleView, handleEdit, handleDelete, t]);
|
|
365
|
+
const applyFilters = useCallback(() => {
|
|
366
|
+
context.dispatch({
|
|
367
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_CURRENT_PAGE,
|
|
368
|
+
payload: { currentPage: 1 },
|
|
369
|
+
});
|
|
370
|
+
listFetchNow();
|
|
371
|
+
handleCloseDrawer();
|
|
372
|
+
}, [context, listFetchNow, handleCloseDrawer]);
|
|
373
|
+
// ============================================================================
|
|
374
|
+
// EFFECTS
|
|
375
|
+
// ============================================================================
|
|
376
|
+
useEffect(() => {
|
|
377
|
+
if (!(workspace === null || workspace === void 0 ? void 0 : workspace.id))
|
|
378
|
+
return;
|
|
379
|
+
(async () => {
|
|
380
|
+
try {
|
|
381
|
+
const { count, items } = await getCachedDiscountCodes({
|
|
382
|
+
params: listParams,
|
|
383
|
+
});
|
|
384
|
+
context.dispatch({
|
|
385
|
+
type: DISCOUNT_CODE_ACTION_TYPES.SET_ITEMS,
|
|
386
|
+
payload: { items: items || [], count: count || 0 },
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
catch (_a) {
|
|
390
|
+
showToast(t("messagesFetchFailed"), TOAST_VARIANT.ERROR);
|
|
391
|
+
}
|
|
392
|
+
})();
|
|
393
|
+
}, [listParams, showToast, t, workspace === null || workspace === void 0 ? void 0 : workspace.id, context]);
|
|
394
|
+
// ============================================================================
|
|
395
|
+
// RETURN
|
|
396
|
+
// ============================================================================
|
|
397
|
+
return Object.assign(Object.assign({}, context), { applyFilters,
|
|
398
|
+
byIdLoading,
|
|
399
|
+
clearFilters,
|
|
400
|
+
deleteLoading,
|
|
401
|
+
handleChange,
|
|
402
|
+
handleCloseDrawer,
|
|
403
|
+
handleCreate,
|
|
404
|
+
handleDelete,
|
|
405
|
+
handleEdit,
|
|
406
|
+
handleFilters,
|
|
407
|
+
handlePageChange,
|
|
408
|
+
handlePageLimitChange,
|
|
409
|
+
handleSearch,
|
|
410
|
+
handleSubmit,
|
|
411
|
+
handleView,
|
|
412
|
+
headerActions,
|
|
413
|
+
listLoading,
|
|
414
|
+
rowActions,
|
|
415
|
+
updateLoading });
|
|
416
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DiscountCodeFilter
|
|
3
|
+
*
|
|
4
|
+
* Lightweight filter UI for the discount-code drawer. The generic page
|
|
5
|
+
* will render this component inside the filter drawer slot. Keep filters
|
|
6
|
+
* minimal and explicit (enabled flag, type select) so caching remains
|
|
7
|
+
* predictable.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DiscountCodeFilter: () => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslations } from "next-intl";
|
|
3
|
+
/**
|
|
4
|
+
* DiscountCodeFilter
|
|
5
|
+
*
|
|
6
|
+
* Lightweight filter UI for the discount-code drawer. The generic page
|
|
7
|
+
* will render this component inside the filter drawer slot. Keep filters
|
|
8
|
+
* minimal and explicit (enabled flag, type select) so caching remains
|
|
9
|
+
* predictable.
|
|
10
|
+
*/
|
|
11
|
+
export const DiscountCodeFilter = () => {
|
|
12
|
+
const t = useTranslations("discountCode");
|
|
13
|
+
return (_jsx("div", { className: "space-y-4", children: _jsx("div", { className: "text-sm text-muted-foreground", children: t("filter") }) }));
|
|
14
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const DiscountCodeForm: () => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Discount Code Form
|
|
5
|
+
*
|
|
6
|
+
* Controlled form component used inside the generic module drawer.
|
|
7
|
+
* Fields in this form map directly to the `accountDiscountCodeFormValidation`
|
|
8
|
+
* schema in `validate.ts` and to the `update` payload sent to the API.
|
|
9
|
+
*
|
|
10
|
+
* Guidelines:
|
|
11
|
+
* - Use `handleChange(field, value)` from `useDiscountCodeModule()` to
|
|
12
|
+
* update the local module state.
|
|
13
|
+
* - Keep input `id` props stable to preserve accessibility and testing hooks.
|
|
14
|
+
*/
|
|
15
|
+
import { useDiscountCodeModule } from "./context";
|
|
16
|
+
import { useTranslations } from "next-intl";
|
|
17
|
+
import { EnhancedInput } from "@appcorp/shadcn/components/enhanced-input";
|
|
18
|
+
import { EnhancedTextarea } from "@appcorp/shadcn/components/enhanced-textarea";
|
|
19
|
+
import { EnhancedCombobox } from "@appcorp/shadcn/components/enhanced-combobox";
|
|
20
|
+
import { EnhancedCheckbox } from "@appcorp/shadcn/components/enhanced-checkbox";
|
|
21
|
+
import { Percent, DollarSign } from "lucide-react";
|
|
22
|
+
import { DISCOUNT_TYPE } from "@/type";
|
|
23
|
+
export const DiscountCodeForm = () => {
|
|
24
|
+
const { state, handleChange } = useDiscountCodeModule();
|
|
25
|
+
const t = useTranslations("discountCode");
|
|
26
|
+
const { code, description, discountType, discountValue, enabled, errors } = state;
|
|
27
|
+
const discountTypeOptions = [
|
|
28
|
+
{
|
|
29
|
+
label: t("formDiscountTypeOptionsPercentage"),
|
|
30
|
+
value: DISCOUNT_TYPE.PERCENTAGE,
|
|
31
|
+
icon: _jsx(Percent, { className: "h-4 w-4" }),
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
label: t("formDiscountTypeOptionsFixed"),
|
|
35
|
+
value: DISCOUNT_TYPE.FIXED,
|
|
36
|
+
icon: _jsx(DollarSign, { className: "h-4 w-4" }),
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
return (_jsx("div", { className: "space-y-6", children: _jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsx(EnhancedInput, { error: errors.code, id: "code", info: t("formDiscountCodeInfo"), label: t("formDiscountCodeLabel"), onChange: (e) => handleChange("code", e.target.value.toUpperCase()), placeholder: t("formDiscountCodePlaceholder"), required: true, value: code }), _jsx(EnhancedTextarea, { error: errors.description, id: "description", info: t("formDescriptionInfo"), label: t("formDescriptionLabel"), onChange: (e) => handleChange("description", e.target.value), placeholder: t("formDescriptionPlaceholder"), rows: 3, value: description || "" }), _jsx(EnhancedCombobox, { error: errors.discountType, id: "discountType", info: t("formDiscountTypeInfo"), label: t("formDiscountTypeLabel"), onValueChange: (value) => handleChange("discountType", value), options: discountTypeOptions, placeholder: t("formDiscountTypePlaceholder"), required: true, value: discountType }), _jsx(EnhancedInput, { id: "discountValue", label: t("formDiscountValueLabel"), type: "number", value: (discountValue === null || discountValue === void 0 ? void 0 : discountValue.toString()) || "", onChange: (e) => handleChange("discountValue", parseFloat(e.target.value) || 0), error: errors.discountValue, placeholder: discountType === DISCOUNT_TYPE.PERCENTAGE
|
|
40
|
+
? t("formDiscountValuePlaceholderPercentage")
|
|
41
|
+
: t("formDiscountValuePlaceholderFixed"), required: true, min: 0, max: discountType === DISCOUNT_TYPE.PERCENTAGE ? 100 : undefined, step: discountType === DISCOUNT_TYPE.PERCENTAGE ? 0.1 : 0.01, info: discountType === DISCOUNT_TYPE.PERCENTAGE
|
|
42
|
+
? t("formDiscountValueInfoPercentage")
|
|
43
|
+
: t("formDiscountValueInfoFixed") }), _jsx(EnhancedCheckbox, { checked: enabled, id: "enabled", info: t("formEnabledInfo"), label: t("formEnabledLabel"), onCheckedChange: (checked) => handleChange("enabled", checked) })] }) }));
|
|
44
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* DiscountCodeMoreActions
|
|
4
|
+
*
|
|
5
|
+
* Placeholder slot rendered inside the generic page's "more actions"
|
|
6
|
+
* area. Keep this file small — add any contextual bulk-actions here
|
|
7
|
+
* (export/import, bulk enable/disable) as the product requires.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DiscountCodeMoreActions: React.FC;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslations } from "next-intl";
|
|
3
|
+
/**
|
|
4
|
+
* DiscountCodeMoreActions
|
|
5
|
+
*
|
|
6
|
+
* Placeholder slot rendered inside the generic page's "more actions"
|
|
7
|
+
* area. Keep this file small — add any contextual bulk-actions here
|
|
8
|
+
* (export/import, bulk enable/disable) as the product requires.
|
|
9
|
+
*/
|
|
10
|
+
export const DiscountCodeMoreActions = () => {
|
|
11
|
+
const t = useTranslations("discountCode");
|
|
12
|
+
return (_jsx("div", { className: "space-y-4", children: _jsx("div", { className: "text-sm text-muted-foreground", children: t("moreActions") }) }));
|
|
13
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discount Code 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
|
+
* - discountCodeConfig (memoised on locale change only; size fixed so the
|
|
8
|
+
* 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 DiscountCodePage: FC<{
|
|
15
|
+
userRole: USER_ROLE;
|
|
16
|
+
}>;
|