@cundi/refine-xaf 1.0.0

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 ADDED
@@ -0,0 +1,267 @@
1
+ # @cundi/refine-xaf
2
+
3
+ This is an SDK integrating XAF backend with Refine frontend, including core logic for Authentication (Auth Provider), Data Access (Data Provider), and a complete UI component library.
4
+
5
+ ## Features
6
+
7
+ - **Auth Provider**: Handles login, logout, Token management, and permission checks.
8
+ - **Data Provider**: Data access layer designed specifically for XAF OData.
9
+ - **UI Components**:
10
+ - `Header`: Application header including user menu and theme toggle (Dark/Light Mode).
11
+ - `LoginPage`: Standard login page.
12
+ - `SmartList`, `RelatedList`: Highly encapsulated generic list and detail components.
13
+ - `TiptapEditor`: Rich text editor with support for Images, Tables, Tasks, Math (LaTeX), YouTube, Emoji, Highlight, and Text Color.
14
+ - `ApplicationUser`: Complete user management (List, Create, Edit, Role Assignment).
15
+ - `PermissionPolicyRole`: Complete role and permission management.
16
+
17
+ ## How to use in a new project
18
+
19
+ ### 1. Initialize Project
20
+
21
+ It is recommended to use the official tool to create a standard Refine + Vite + Ant Design project:
22
+
23
+ ```bash
24
+ npm create refine-app@latest my-project
25
+ # Recommended options:
26
+ # Backend: Custom JSON REST (will be replaced later)
27
+ # UI Framework: Ant Design
28
+ # Authentication: None (will use SDK later)
29
+ ```
30
+
31
+ ### 2. Install SDK
32
+
33
+ Install this SDK in your project directory:
34
+
35
+ ```bash
36
+ # If in the same level as packages folder (monorepo structure)
37
+ npm install ../packages/refine-xaf
38
+
39
+ # Or use the published package name
40
+ # npm install @cundi/refine-xaf
41
+ ```
42
+
43
+ ### 3. Setup Environment Variables (.env)
44
+
45
+ Create a `.env` file in the project root directory and specify the backend API location.
46
+
47
+ > **Note**: SDK's `httpClient` (used for Auth) defaults to reading `VITE_API_URL`. OData endpoints usually need the `/odata` suffix.
48
+
49
+ ```env
50
+ VITE_API_URL=https://localhost:7087/api
51
+ ```
52
+
53
+ ### 4. Setup App.tsx
54
+
55
+ Modify `src/App.tsx` to import components and logic from the SDK:
56
+
57
+ ```tsx
58
+ import React from "react";
59
+ import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";
60
+ import { App as AntdApp, ConfigProvider, theme } from "antd";
61
+ import { Refine, Authenticated } from "@refinedev/core";
62
+ import { ThemedLayout, ErrorComponent, RefineThemes, useNotificationProvider } from "@refinedev/antd";
63
+ import routerProvider, { NavigateToResource, CatchAllNavigate, UnsavedChangesNotifier, DocumentTitleHandler } from "@refinedev/react-router";
64
+ import "@refinedev/antd/dist/reset.css";
65
+
66
+ // 1. Import SDK
67
+ import {
68
+ authProvider,
69
+ dataProvider,
70
+ Header,
71
+ LoginPage,
72
+ ApplicationUserList,
73
+ ApplicationUserCreate,
74
+ ApplicationUserEdit,
75
+ RoleList,
76
+ RoleCreate,
77
+ RoleEdit,
78
+ ColorModeContextProvider,
79
+ useColorMode
80
+ } from "@cundi/refine-xaf";
81
+
82
+ // 2. Setup API URL (Raw URL for Auth, /odata for Data)
83
+ const API_URL = import.meta.env.VITE_API_URL;
84
+
85
+ const InnerApp: React.FC = () => {
86
+ const { mode } = useColorMode();
87
+
88
+ return (
89
+ <BrowserRouter>
90
+ <ConfigProvider
91
+ theme={{
92
+ ...RefineThemes.Blue,
93
+ algorithm: mode === "dark" ? theme.darkAlgorithm : theme.defaultAlgorithm,
94
+ }}
95
+ >
96
+ <AntdApp>
97
+ <Refine
98
+ authProvider={authProvider}
99
+ // 3. Setup Data Provider (Note the /odata suffix)
100
+ dataProvider={dataProvider(API_URL + "/odata")}
101
+ routerProvider={routerProvider}
102
+ notificationProvider={useNotificationProvider}
103
+ resources={[
104
+ {
105
+ name: "dashboard",
106
+ list: "/",
107
+ meta: { label: "Dashboard" }
108
+ },
109
+ {
110
+ name: "ApplicationUser",
111
+ list: "/ApplicationUsers",
112
+ create: "/ApplicationUsers/create",
113
+ edit: "/ApplicationUsers/edit/:id",
114
+ meta: { label: "Users" }
115
+ },
116
+ {
117
+ name: "PermissionPolicyRole",
118
+ list: "/PermissionPolicyRoles",
119
+ create: "/PermissionPolicyRoles/create",
120
+ edit: "/PermissionPolicyRoles/edit/:id",
121
+ meta: { label: "Roles" }
122
+ }
123
+ ]}
124
+ >
125
+ <Routes>
126
+ <Route
127
+ element={
128
+ <Authenticated key="authenticated-routes" fallback={<CatchAllNavigate to="/login" />}>
129
+ <ThemedLayout Header={Header}>
130
+ <Outlet />
131
+ </ThemedLayout>
132
+ </Authenticated>
133
+ }
134
+ >
135
+ <Route index element={<div>Welcome to Dashboard</div>} />
136
+
137
+ {/* 4. Setup pages provided by SDK */}
138
+ <Route path="/ApplicationUsers">
139
+ <Route index element={<ApplicationUserList />} />
140
+ <Route path="create" element={<ApplicationUserCreate />} />
141
+ <Route path="edit/:id" element={<ApplicationUserEdit />} />
142
+ </Route>
143
+
144
+ <Route path="/PermissionPolicyRoles">
145
+ <Route index element={<RoleList />} />
146
+ <Route path="create" element={<RoleCreate />} />
147
+ <Route path="edit/:id" element={<RoleEdit />} />
148
+ </Route>
149
+ </Route>
150
+
151
+ <Route
152
+ element={
153
+ <Authenticated key="auth-pages" fallback={<Outlet />}>
154
+ <NavigateToResource resource="dashboard" />
155
+ </Authenticated>
156
+ }
157
+ >
158
+ <Route path="/login" element={<LoginPage />} />
159
+ </Route>
160
+ </Routes>
161
+ <UnsavedChangesNotifier />
162
+ <DocumentTitleHandler />
163
+ </Refine>
164
+ </AntdApp>
165
+ </ConfigProvider>
166
+ </BrowserRouter>
167
+ );
168
+ };
169
+
170
+ const App: React.FC = () => {
171
+ return (
172
+ <ColorModeContextProvider>
173
+ <InnerApp />
174
+ </ColorModeContextProvider>
175
+ );
176
+ };
177
+
178
+ export default App;
179
+ ```
180
+
181
+ ## Building Custom CRUD Pages
182
+
183
+ To create CRUD pages for your own XAF business objects (e.g., `DemoObject`), follow these patterns (reference `cundiweb/src/pages/demo-objects` for complete examples).
184
+
185
+ ### List Page
186
+
187
+ Use the `SmartList` component to quickly build a feature-rich list view with search capabilities.
188
+
189
+ ```tsx
190
+ import { SmartList } from "@cundi/refine-xaf";
191
+ import { Table, Checkbox, Space } from "antd";
192
+ import { EditButton, ShowButton, DeleteButton, DateField } from "@refinedev/antd";
193
+
194
+ export const DemoObjectList = () => {
195
+ return (
196
+ // searchFields prop enables the search bar for specified columns
197
+ <SmartList searchFields={["Name", "StringValue"]}>
198
+ <Table.Column dataIndex="Name" title="Name" sorter defaultVisible />
199
+ <Table.Column dataIndex="StringValue" title="String Value" sorter />
200
+ <Table.Column
201
+ dataIndex="BoolValue"
202
+ title="Boolean"
203
+ render={(value) => <Checkbox checked={value} disabled />}
204
+ />
205
+ {/* Standard Ant Design Table.Column configuration */}
206
+ <Table.Column
207
+ title="Actions"
208
+ dataIndex="actions"
209
+ render={(_, record) => (
210
+ <Space>
211
+ <EditButton hideText size="small" recordItemId={record.Oid} />
212
+ <ShowButton hideText size="small" recordItemId={record.Oid} />
213
+ <DeleteButton hideText size="small" recordItemId={record.Oid} />
214
+ </Space>
215
+ )}
216
+ />
217
+ </SmartList>
218
+ );
219
+ };
220
+ ```
221
+
222
+ ### Create/Edit Page
223
+
224
+ Use standard Refine hooks (`useForm`) combined with Ant Design Form components. For file uploads (like Images), use the `Base64Upload` component provided by the SDK.
225
+
226
+ ```tsx
227
+ import { Create, useForm } from "@refinedev/antd"; // or Edit
228
+ import { Base64Upload } from "@cundi/refine-xaf";
229
+ import { Form, Input, Switch } from "antd";
230
+
231
+ export const DemoObjectCreate = () => {
232
+ const { formProps, saveButtonProps } = useForm();
233
+
234
+ return (
235
+ <Create saveButtonProps={saveButtonProps}>
236
+ <Form {...formProps} layout="vertical">
237
+ <Form.Item
238
+ label="Name"
239
+ name={["Name"]}
240
+ rules={[{ required: true }]}
241
+ >
242
+ <Input />
243
+ </Form.Item>
244
+
245
+ {/* Image upload example */}
246
+ <Form.Item label="Image" name={["ImageValue"]}>
247
+ <Base64Upload />
248
+ </Form.Item>
249
+
250
+ <Form.Item
251
+ label="Active"
252
+ name={["Active"]}
253
+ valuePropName="checked"
254
+ >
255
+ <Switch />
256
+ </Form.Item>
257
+ </Form>
258
+ </Create>
259
+ );
260
+ };
261
+ ```
262
+
263
+ ## Development and Publishing
264
+
265
+ 1. **Install Dependencies**: `npm install`
266
+ 2. **Build SDK**: `npm run build`
267
+ 3. **Development Mode**: `npm run dev`
@@ -0,0 +1,140 @@
1
+ import { AuthProvider, DataProvider, BaseRecord, IResourceComponentsProps } from '@refinedev/core';
2
+ import React, { PropsWithChildren } from 'react';
3
+ import * as _tanstack_react_query from '@tanstack/react-query';
4
+
5
+ declare const authProvider: AuthProvider;
6
+
7
+ declare const dataProvider: (apiUrl: string) => DataProvider;
8
+
9
+ interface IApplicationUser {
10
+ Oid: string;
11
+ UserName: string;
12
+ DisplayName: string;
13
+ Email: string;
14
+ Photo?: string;
15
+ IsActive: boolean;
16
+ AccessFailedCount: number;
17
+ LockoutEnd?: string;
18
+ Roles?: IPermissionPolicyRole[];
19
+ }
20
+ declare enum SecurityPermissionPolicy {
21
+ DenyAllByDefault = "DenyAllByDefault",
22
+ ReadOnlyAllByDefault = "ReadOnlyAllByDefault",
23
+ AllowAllByDefault = "AllowAllByDefault"
24
+ }
25
+ declare enum SecurityPermissionState {
26
+ Allow = "Allow",
27
+ Deny = "Deny"
28
+ }
29
+ interface IPermissionPolicyTypePermissionObject {
30
+ Oid?: string;
31
+ TargetType: string;
32
+ ReadState?: SecurityPermissionState;
33
+ WriteState?: SecurityPermissionState;
34
+ CreateState?: SecurityPermissionState;
35
+ DeleteState?: SecurityPermissionState;
36
+ NavigateState?: SecurityPermissionState;
37
+ }
38
+ interface IPermissionPolicyRole {
39
+ Oid: string;
40
+ Name: string;
41
+ IsAdministrative: boolean;
42
+ PermissionPolicy: SecurityPermissionPolicy;
43
+ TypePermissions?: IPermissionPolicyTypePermissionObject[];
44
+ }
45
+
46
+ declare const TOKEN_KEY = "refine-auth";
47
+ declare class HttpError extends Error {
48
+ statusCode: number;
49
+ message: string;
50
+ body: any;
51
+ constructor(statusCode: number, message: string, body: any);
52
+ }
53
+ interface RequestOptions extends RequestInit {
54
+ skipAuth?: boolean;
55
+ }
56
+ declare const getBaseUrl: () => any;
57
+ declare const httpClient: (endpoint: string, options?: RequestOptions) => Promise<Response | null>;
58
+ declare const parseJwt: (token: string) => any;
59
+
60
+ declare const authService: {
61
+ login: ({ username, password }: any) => Promise<string>;
62
+ getUserByUsername: (username: string) => Promise<IApplicationUser | null>;
63
+ getUserById: (userId: string) => Promise<IApplicationUser | null>;
64
+ resetPassword: (userId: string, newPassword: string) => Promise<boolean>;
65
+ };
66
+
67
+ declare const Header: React.FC;
68
+
69
+ interface SmartListProps {
70
+ children?: React.ReactNode;
71
+ resource?: string;
72
+ searchFields?: string[];
73
+ }
74
+ declare const SmartList: <T extends BaseRecord = BaseRecord>({ children, resource, searchFields, }: SmartListProps) => React.JSX.Element;
75
+
76
+ interface RelatedListProps<TItem extends BaseRecord> {
77
+ resource: string;
78
+ /** Field name in the detail object that links to master (e.g. "Master") */
79
+ masterField: string;
80
+ /** ID of the master record */
81
+ masterId?: string;
82
+ /** Data source for the table */
83
+ dataSource?: TItem[];
84
+ /** Callback when data changes (save/delete) to refresh parent */
85
+ onMutationSuccess?: () => void;
86
+ /** Function component to render form fields. Receives 'mode' prop. */
87
+ FormFields: any;
88
+ /** Title for the modal, defaults to "Manage Detail" */
89
+ modalTitle?: string;
90
+ /** Extra props for the Table */
91
+ children?: React.ReactNode;
92
+ }
93
+ declare const RelatedList: <TItem extends BaseRecord>({ resource, masterField, masterId, dataSource, onMutationSuccess, FormFields, modalTitle, children, }: RelatedListProps<TItem>) => React.JSX.Element;
94
+
95
+ interface Base64UploadProps {
96
+ value?: string;
97
+ onChange?: (value: string) => void;
98
+ }
99
+ declare const Base64Upload: React.FC<Base64UploadProps>;
100
+
101
+ interface TiptapEditorProps {
102
+ value?: string;
103
+ onChange?: (value: string) => void;
104
+ disabled?: boolean;
105
+ }
106
+ declare const TiptapEditor: React.FC<TiptapEditorProps>;
107
+
108
+ declare const LoginPage: React.FC;
109
+
110
+ declare const ApplicationUserList: React.FC<IResourceComponentsProps>;
111
+
112
+ declare const ApplicationUserCreate: React.FC<IResourceComponentsProps>;
113
+
114
+ declare const ApplicationUserEdit: React.FC<IResourceComponentsProps>;
115
+
116
+ declare const RoleList: React.FC<IResourceComponentsProps>;
117
+
118
+ declare const RoleCreate: React.FC<IResourceComponentsProps>;
119
+
120
+ declare const RoleEdit: React.FC<IResourceComponentsProps>;
121
+
122
+ type ColorModeContextType = {
123
+ mode: "light" | "dark";
124
+ setMode: (mode: "light" | "dark") => void;
125
+ };
126
+ declare const ColorModeContext: React.Context<ColorModeContextType>;
127
+ declare const ColorModeContextProvider: React.FC<PropsWithChildren>;
128
+ declare const useColorMode: () => ColorModeContextType;
129
+
130
+ interface IModelType {
131
+ Name: string;
132
+ Caption: string;
133
+ IsCreatable: boolean;
134
+ IsDeprecated: boolean;
135
+ Value?: string;
136
+ Label?: string;
137
+ }
138
+ declare const useModelTypes: () => _tanstack_react_query.UseQueryResult<IModelType[], Error>;
139
+
140
+ export { ApplicationUserCreate, ApplicationUserEdit, ApplicationUserList, Base64Upload, ColorModeContext, ColorModeContextProvider, Header, HttpError, type IApplicationUser, type IModelType, type IPermissionPolicyRole, type IPermissionPolicyTypePermissionObject, LoginPage, RelatedList, type RequestOptions, RoleCreate, RoleEdit, RoleList, SecurityPermissionPolicy, SecurityPermissionState, SmartList, TOKEN_KEY, TiptapEditor, type TiptapEditorProps, authProvider, authService, dataProvider, getBaseUrl, httpClient, parseJwt, useColorMode, useModelTypes };
@@ -0,0 +1,140 @@
1
+ import { AuthProvider, DataProvider, BaseRecord, IResourceComponentsProps } from '@refinedev/core';
2
+ import React, { PropsWithChildren } from 'react';
3
+ import * as _tanstack_react_query from '@tanstack/react-query';
4
+
5
+ declare const authProvider: AuthProvider;
6
+
7
+ declare const dataProvider: (apiUrl: string) => DataProvider;
8
+
9
+ interface IApplicationUser {
10
+ Oid: string;
11
+ UserName: string;
12
+ DisplayName: string;
13
+ Email: string;
14
+ Photo?: string;
15
+ IsActive: boolean;
16
+ AccessFailedCount: number;
17
+ LockoutEnd?: string;
18
+ Roles?: IPermissionPolicyRole[];
19
+ }
20
+ declare enum SecurityPermissionPolicy {
21
+ DenyAllByDefault = "DenyAllByDefault",
22
+ ReadOnlyAllByDefault = "ReadOnlyAllByDefault",
23
+ AllowAllByDefault = "AllowAllByDefault"
24
+ }
25
+ declare enum SecurityPermissionState {
26
+ Allow = "Allow",
27
+ Deny = "Deny"
28
+ }
29
+ interface IPermissionPolicyTypePermissionObject {
30
+ Oid?: string;
31
+ TargetType: string;
32
+ ReadState?: SecurityPermissionState;
33
+ WriteState?: SecurityPermissionState;
34
+ CreateState?: SecurityPermissionState;
35
+ DeleteState?: SecurityPermissionState;
36
+ NavigateState?: SecurityPermissionState;
37
+ }
38
+ interface IPermissionPolicyRole {
39
+ Oid: string;
40
+ Name: string;
41
+ IsAdministrative: boolean;
42
+ PermissionPolicy: SecurityPermissionPolicy;
43
+ TypePermissions?: IPermissionPolicyTypePermissionObject[];
44
+ }
45
+
46
+ declare const TOKEN_KEY = "refine-auth";
47
+ declare class HttpError extends Error {
48
+ statusCode: number;
49
+ message: string;
50
+ body: any;
51
+ constructor(statusCode: number, message: string, body: any);
52
+ }
53
+ interface RequestOptions extends RequestInit {
54
+ skipAuth?: boolean;
55
+ }
56
+ declare const getBaseUrl: () => any;
57
+ declare const httpClient: (endpoint: string, options?: RequestOptions) => Promise<Response | null>;
58
+ declare const parseJwt: (token: string) => any;
59
+
60
+ declare const authService: {
61
+ login: ({ username, password }: any) => Promise<string>;
62
+ getUserByUsername: (username: string) => Promise<IApplicationUser | null>;
63
+ getUserById: (userId: string) => Promise<IApplicationUser | null>;
64
+ resetPassword: (userId: string, newPassword: string) => Promise<boolean>;
65
+ };
66
+
67
+ declare const Header: React.FC;
68
+
69
+ interface SmartListProps {
70
+ children?: React.ReactNode;
71
+ resource?: string;
72
+ searchFields?: string[];
73
+ }
74
+ declare const SmartList: <T extends BaseRecord = BaseRecord>({ children, resource, searchFields, }: SmartListProps) => React.JSX.Element;
75
+
76
+ interface RelatedListProps<TItem extends BaseRecord> {
77
+ resource: string;
78
+ /** Field name in the detail object that links to master (e.g. "Master") */
79
+ masterField: string;
80
+ /** ID of the master record */
81
+ masterId?: string;
82
+ /** Data source for the table */
83
+ dataSource?: TItem[];
84
+ /** Callback when data changes (save/delete) to refresh parent */
85
+ onMutationSuccess?: () => void;
86
+ /** Function component to render form fields. Receives 'mode' prop. */
87
+ FormFields: any;
88
+ /** Title for the modal, defaults to "Manage Detail" */
89
+ modalTitle?: string;
90
+ /** Extra props for the Table */
91
+ children?: React.ReactNode;
92
+ }
93
+ declare const RelatedList: <TItem extends BaseRecord>({ resource, masterField, masterId, dataSource, onMutationSuccess, FormFields, modalTitle, children, }: RelatedListProps<TItem>) => React.JSX.Element;
94
+
95
+ interface Base64UploadProps {
96
+ value?: string;
97
+ onChange?: (value: string) => void;
98
+ }
99
+ declare const Base64Upload: React.FC<Base64UploadProps>;
100
+
101
+ interface TiptapEditorProps {
102
+ value?: string;
103
+ onChange?: (value: string) => void;
104
+ disabled?: boolean;
105
+ }
106
+ declare const TiptapEditor: React.FC<TiptapEditorProps>;
107
+
108
+ declare const LoginPage: React.FC;
109
+
110
+ declare const ApplicationUserList: React.FC<IResourceComponentsProps>;
111
+
112
+ declare const ApplicationUserCreate: React.FC<IResourceComponentsProps>;
113
+
114
+ declare const ApplicationUserEdit: React.FC<IResourceComponentsProps>;
115
+
116
+ declare const RoleList: React.FC<IResourceComponentsProps>;
117
+
118
+ declare const RoleCreate: React.FC<IResourceComponentsProps>;
119
+
120
+ declare const RoleEdit: React.FC<IResourceComponentsProps>;
121
+
122
+ type ColorModeContextType = {
123
+ mode: "light" | "dark";
124
+ setMode: (mode: "light" | "dark") => void;
125
+ };
126
+ declare const ColorModeContext: React.Context<ColorModeContextType>;
127
+ declare const ColorModeContextProvider: React.FC<PropsWithChildren>;
128
+ declare const useColorMode: () => ColorModeContextType;
129
+
130
+ interface IModelType {
131
+ Name: string;
132
+ Caption: string;
133
+ IsCreatable: boolean;
134
+ IsDeprecated: boolean;
135
+ Value?: string;
136
+ Label?: string;
137
+ }
138
+ declare const useModelTypes: () => _tanstack_react_query.UseQueryResult<IModelType[], Error>;
139
+
140
+ export { ApplicationUserCreate, ApplicationUserEdit, ApplicationUserList, Base64Upload, ColorModeContext, ColorModeContextProvider, Header, HttpError, type IApplicationUser, type IModelType, type IPermissionPolicyRole, type IPermissionPolicyTypePermissionObject, LoginPage, RelatedList, type RequestOptions, RoleCreate, RoleEdit, RoleList, SecurityPermissionPolicy, SecurityPermissionState, SmartList, TOKEN_KEY, TiptapEditor, type TiptapEditorProps, authProvider, authService, dataProvider, getBaseUrl, httpClient, parseJwt, useColorMode, useModelTypes };